通过httpclient比较重定向和请求转发

前言:刚开始学习Java的web编程时,都会碰到重定向请求转发这两个概念,而且一般也会特别强调二者的区别。当时,因为是刚接触,所以也就似懂非懂的接受了,然后记得编程的时候要注意二者的区别。学习完计算机网络后,对这两个概念的理解也更加深刻了,主要是具有了网络的层次概念,以及应用层的基本知识。最近使用 httpclient,发生了一个错误,我请求一个网站地址,发现并没有收到正确的响应,最后发现是因为发生了重定向,但是 httpclient 并没有帮我自动重定向。发现这个问题还挺有趣的,所以就来再深入了解一下。

这里介绍一下:HttpClient 4.x 版本,get请求方法会自动进行重定向,而post请求方法不会自动进行重定向,这是要注意的地方。我上次发生错误,就是使用post提交表单登录,当时没有自动重定向。



请求转发和重定向的区别

1、重定向是两次请求,转发是一次请求,因此转发的速度要快于重定向。
2、重定向之后地址栏上的地址会发生变化,变化成第二次请求的地址,转发之后地址栏上的地址不会变化,还是第一次请求的地址。
3、转发是服务器行为,重定向是客户端行为。重定向时浏览器上的网址改变 ,转发是浏览器上的网址不变。
4、重定向是两次request,转发只有一次请求。
5、重定向时的网址可以是任何网址,转发的网址必须是本站点的网址。

这里重点看第三条和第四条。

HTTP报文包含响应码、响应头和响应体。
这里只说 200 和 302.
响应码:2xx(一般是200)。表示请求成功,然后就可以接受响应的数据。
响应码:3xx(一般为302)。表示重定向,服务器会要求客户端重新发送一个请求,服务器会发送一个响应头 Location,它指定了新请求的 URL 地址。(这里很重要!客户端需要通过 Location 头,获取重定向的地址。

这里并没有提及请求转发,因为请求转发是一种服务器端的操作,它是在服务器内部进行操作的,所以就是一次请求(在客户端看和普通的请求没有区别)。而重定向不同,它是客户端的操作,因为服务器要求客户端重新发送一次请求,所以重定向是两次请求。这也解释了为什么重定向后请求参数会丢失,因为根本不是一次请求。我这里说客户端,并不说浏览器,因为我们有时不一定会使用浏览器作为客户端,比如爬虫也是是一个客户端了。

但是,对于刚开始学习的时候,或者对于普通用户来说,似乎感觉不出来二者的区别,最多是可以发现浏览器地址是否改变。请求转发浏览器地址不变,而重定向浏览器地址会变化为新的地址。(但是在这个过程中并未体现重定向的两次请求,这是因为浏览器自动帮我们进行了重定向。)

下面以Java编程来看看二者的区别:上面的第三条和第四条

Java web 部分

TestServlet 类

提供一个简单的 Servlet 类,它的功能和简单,它会携带一个 key 参数,如果该参数存在且值为 “1”,那么就进行请求转发到 “/dispatcher.jsp” 页面;否则,就重定向到 “redirect.jsp” 页面。

package com.study;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/TestServlet")
public class TestServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
    
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		String key = request.getParameter("key");
		if((key != null && key.equals("1"))) {
			System.out.println("请求转发:key " + key);
			//请求转发
			request.getRequestDispatcher("/dispatcher.jsp").forward(request,response);
		}else {
			//重定向
			response.sendRedirect("redirect.jsp");
			System.out.println("重定向:key " + key);
		}
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}

}

dispacher.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>请求转发</title>
</head>
<body>
<h1>请求转发</h1>
</body>
</html>

redirect.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>重定向</title>
</head>
<body>
<h1>重定向</h1>
</body>
</html>

启动项目测试

我这里的目的主要是请求转发和重定向的访问区别,并不是比较它们二者的其它区别,所以示例很简单。
测试请求地址:http://localhost:8080/study/TestServlet?key=1
注意:请求地址无变化。
在这里插入图片描述

测试请求地址:http://localhost:8080/study/TestServlet?key=2
注意:请求地址发生了变化。
在这里插入图片描述

这样的话,是看不出来二者在访问上有什么区别的,但是看不到,不代表它们没有区别,下面通过代码来查看二者在访问方式上的区别。

使用 HttpClient

301 Moved Permanently(永久移动)
302 Found(发现)
303 See Other(查看其他)
307 Temporary Redirect(临时重定向)

由于 HttpClient 4.x 版本会自动重定向,所以我们必须关闭自动重定向,才能跟踪重定向的过程。

在 RequestConfig 里面设置,并设置超时时间(三个)。

//HttpClient4.3中默认允许自动重定向,导致程序中不能跟踪跳转情况。
int timeout = 10*1000;
RequestConfig config = RequestConfig.custom()
		.setSocketTimeout(timeout)
		.setConnectTimeout(timeout)
		.setConnectionRequestTimeout(timeout)
		.setRedirectsEnabled(false)   //关闭自动重定向,默认值为true。
		.build();

然后发送请求时,设置并允许自动重定向。

//创建get请求对象
HttpGet getMethod = new HttpGet(url);
//设置请求方法关闭自动重定向
getMethod.setConfig(config);  //配置信息

这样当我们访问路径为:http://localhost:8080/study/TestServlet?key=1,服务器会进行请求转发,但是这是服务器行为,与客户端没有关系,所以我们不用关心,在请求正确的情况下,响应码为 200。

测试结果:
在这里插入图片描述

当我们访问路径为:http://localhost:8080/study/TestServlet?key=2,服务器会要求客户端进行重定向(即要求客户端请求另一个地址),这时会先收到状态码 302,当再次访问成功时状态码为 200(当然了,也许重定向不止一次,但是浏览器会对重定向次数有限制)。如果发生了重定向,我们需要获取响应头中的 Location 字段,这里面是重定向的地址。

//读取新的 URL 地址
Header header = response.getFirstHeader("location");
String newUrl = header.getValue();

注意:重定向是可以访问服务器外的地址的,服务器内部的地址一般是相对地址,需要拼接 URL,服务器外就是绝对 URL 了。

测试结果:
在这里插入图片描述

完整测试代码

package com.learn;

import java.io.IOException;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.ParseException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

public class TestRedirect {
	
	/**
	 * 重定向是客户端操作,而请求转发是服务端操作 。
	 * 但是通常用户使用浏览器,并不注意二者的区别,
	 * 这是因为浏览器自动帮我们重定向了。(当然了,
	 * 编程还是需要注意的)。
	 * @throws IOException 
	 * @throws ParseException 
	 * */
	public static void main(String[] args) throws ParseException, IOException {
		String root = "http://localhost:8080/study/";  //网站的根路径,因为重定向得到的是相对路径(服务器内部的路径)
		
		//HttpClient4.3中默认允许自动重定向,导致程序中不能跟踪跳转情况。
		int timeout = 10*1000;
		RequestConfig config = RequestConfig.custom()
				.setSocketTimeout(timeout)
				.setConnectTimeout(timeout)
				.setConnectionRequestTimeout(timeout)
				.setRedirectsEnabled(false)   //关闭自动重定向,默认值为true。
				.build();
		
		
		String url = "http://localhost:8080/study/TestServlet?key=1";  //请求转发。
		
		//创建 httpclient 对象
		CloseableHttpClient httpClient = HttpClients.createDefault();
		//创建get请求对象
		HttpGet getMethod = new HttpGet(url);
		getMethod.setConfig(config);  //配置信息
	
		//执行请求,得到响应信息
		try (CloseableHttpResponse response = httpClient.execute(getMethod)) {
			HttpEntity entity = null;
			int statusCode = response.getStatusLine().getStatusCode();
			System.out.println("返回值状态码:" + statusCode);
			if (statusCode == HttpStatus.SC_OK) {  
				//获取请求转发的实体信息
				entity = response.getEntity();
				if (entity != null) {
					System.out.println(EntityUtils.toString(entity, "UTF-8"));
				}
			} else if (statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
				
				//读取新的 URL 地址
				Header header = response.getFirstHeader("location");
				System.out.println(header);
				if (header != null) {
					String newUrl = header.getValue();
					if (newUrl != null && !newUrl.equals("")) {
						//使用get方法转向。
						HttpGet redirectGet = new HttpGet(root+newUrl);
						System.out.println("重定向到新的地址:" + redirectGet.getURI());
						redirectGet.setConfig(config);
						//发送请求,做进一步处理。。。
						try (CloseableHttpResponse redirectRes = httpClient.execute(redirectGet)) {
							statusCode = redirectRes.getStatusLine().getStatusCode();
							System.out.println("返回值状态码:" + statusCode);
							if (statusCode == HttpStatus.SC_OK) {  
								//获取请求转发的实体信息
								entity = redirectRes.getEntity();
								if (entity != null) {
									System.out.println(EntityUtils.toString(entity, "UTF-8"));
								}
							}
						}
					}
				}
			} 
		}
	}
}

总结

刚开始学习的时候,对于知识总是一知半解,然后就开始用起来了。但是,随着学习的深入,就会发现自己对与知识的理解加深了,当然了,不断地动手尝试,犯错误才行。因为动手尝试才会熟练,犯错误才会明白为什么?这样是一个比较好地学习方式。我也是某次使用 httpclient 地时候,发现自己没有考虑重定向这个问题,而且 httpclient 也没有帮我自动重定向(POST请求方法不会自动重定向),所以这个问题,就卡住了我很长时间,因为我不知道问题到底出在哪里?最后也是在网上查资料解决的问题,所以就想着试着用 httpclient 加深一下自己对于重定向和请求转发地理解。

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值