HttpURLConnection的重定向处理
我们都知道一般的网络框架都是可以在内部自动处理重定向,比如常见的HttpURLConnection,当我们使用HttpURLConnection来处理重定向时,HttpURLConnection提供给一个 ,setInstanceFollowRedirects(true|false)来设置当前的HttpURLConnection是否在内部处理重定向请求
设置为true:
内部处理重定向,返回最终的响应结果,调用方得到最终的code为200
设置为false:
内部不处理重定向,只返回第一次响应的结果,调用方得到的是301或者302的状态码,调用方需要自己去处理重定向
在某些业务中,业务方需要知道一个链接是否有过重定向,或者业务方有部分请求需要自己处理重定向时,业务方可以自己在构建HttpURLConnection时,灵活的设置setInstanceFollowRedirects
代码如下
URL url = new URL(openurl);
//得到connection对象。
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
//设置请求方式
connection.setRequestMethod(GET);
connection.setConnectTimeout(TurboConstant.OKHTTP_DEFAULT_CONNET_TIMEOUT);
connection.setReadTimeout(TurboConstant.OKHTTP_DEFAULT_REQ_TIMEOUT);
connection.setInstanceFollowRedirects(false);
//连接
connection.connect();
//得到响应码
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_MOVED_PERM || responseCode == HttpURLConnection.HTTP_MOVED_TEMP) {
if (WebTurboConfiguration.getInstance().showAllLog()) {
String redirectUrl = connection.getHeaderField("Location");
TLog.d(TAG, "发现 重定向链接 openurl = " + openurl + " redirectUrl = " + redirectUrl);
} else {
TLog.d(TAG, "find redirect link");
}
} else if (responseCode == HttpURLConnection.HTTP_OK) {
}
connection.disconnect();
OkHttp的重定向处理
尽管现在Android系统提供的HttpURLConnection底层是使用OKhttp实现的,但是如果我们单独使用OKhttp来实现自己的网络请求的化,重定向方面的代码逻辑与HttpURLConnection差异比较大
Okhttp提供了followRedirects(true|false)方法来进行重定向的处理配置,但是followRedirects(true|false)方法是 OkHttpClient实例提供的方法
如图所示
OkHttpClient client = new OkHttpClient.Builder()
.followRedirects(true)
.build();
正常来讲,业务方在使用Okhttp的时候,都会将OkHttpClient做为一个全局的单例来使用,followRedirects做为OkHttpClient提供的方法,一旦设置,就会全局处理或者不处理重定向,这不一定能满足需求
查看OKHttp负责重定向的源码
为了方便理解,我们将核心逻辑提取出来并稍作修改,方便让大家看的更清楚
RetryAndFollowUpInterceptor.java
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
//核心逻辑是一个循环体
while (true) {
//获取网络的数据
Response response = realChain.proceed(request, transmitter, null);
// 判断上一个Response是否有效,如果有效就合入到当前准备返回的Response中
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
//检查是否是一个重定向请求
Request followUp = followUpRequest(response, route);
//不是重定向请求直接返回Response
if (followUp == null) {
return response;
} else {
//是重定向请求,将当前Response保存为priorResponse,在下一个循环
request = followUp;
priorResponse = response;
}
}
}
从RetryAndFollowUpInterceptor.java拦截器的核心逻辑可以看出以下几点
1、当检测到重定向时,RetryAndFollowUpInterceptor保存了重定向发生时的Response,并且命名为priorResponse
2、当重定向后的请求发起后,priorResponse被当成一个参数放入到最终的Response中
因此我们只要在Response中取出priorResponse即可知道曾经发生过什么
因此
我么可以使用如下方法来判断一次请求是否发生过重定向
/**
* 判断是否曾经发生过重定向
* priorResponse是重定向之前产生的response,里面有重定向信息
*
* @param response 响应
* @return 是否发生过重定向
*/
private static boolean isRedirected(Response response) {
Response priorResponse = response.priorResponse();
if (priorResponse != null) {
int priorResponseCode = priorResponse.code();
return priorResponseCode == HttpURLConnection.HTTP_MOVED_PERM || priorResponseCode == HttpURLConnection.HTTP_MOVED_TEMP;
}
return false;
}
在RetryAndFollowUpInterceptor的循环体中,如果一个url请求包含多次重定向,那么包含了priorResponse的Response又会被做为一个新的priorResponse保存起来,因此只要逐级的取出priorResponse就可以得到整个网络请求过程中发生的若干次重定向数据