关于重定向那些事(Java 使用http client 调用带有Redirect 的坑)
项目中使用 Feign 调用 HTTP API 时,出现一个错误:HttpRetryException: cannot retry due to redirection, in streaming mode
feign.RetryableException: cannot retry due to redirection, in streaming mode executing POST <api_url>
at feign.FeignException.errorExecuting(FeignException.java:67)
……
Caused by: java.net.HttpRetryException: cannot retry due to redirection, in streaming mode
at sun.net.www.protocol.http.HttpURLConnection.followRedirect0(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.followRedirect(Unknown Source)
……
由于搜索不到解决方案,自己用Spring MVC 写了一个简单的带有 redirect API,使用feign请求,没有任何问题。首先怀疑是 API 方 (使用python编写)的问题。
于是分别使用 postman 和 JavaScript ajax 调用该接口,发现没有问题。结合异常的意思:“无法在流模式下使用重定向”。
那么问题就出在 Feign 使用的底层 http client 框架,对于重定向的处理策略不同。Feign 默认使用 java.net.HttpURLConnection。更改为 okhttp3 后,正常。于是猜想:
不同的http client 对于重定向的判定策略不同。
接下来根据源码验证了这一猜想。
不同的http client 对于重定向的判定策略不同。
通过排查来验证结论:
一、HttpURLConnection 的重定向策略
通过跟踪 sun.net.www.protocol.http.HttpURLConnection 的源码(followRedirect方法),发现一些重定向的规则:
- 响应状态码码必须是3xx
- 响应头必须包含Location
- location中url的协议与原来的url协议相同。
- 非流模式。(如果使用流模式,直接抛出上面的异常)
- ……
如果通过body发送参数,请求就会是流模式,直接抛出异常。在自己的模拟接口中加上 RequestBody 错误复现。
说明:
1、sun.net.www.protocol.http.HttpURLConnection 是 java.net.HttpURLConnection 子类。
2、使用JDK1.8。
二、Apache HttpClient 的重定向规则
跟踪源码,HttpClient 的重定向规则十分清晰,扩展性强。重定向实现逻辑位于 org.apache.http.impl.execchain.RedirectExec.execute 方法,首先判断配置是否允许重定向然后交给org.apache.http.client.RedirectStrategy 判断。
public CloseableHttpResponse execute( final HttpRoute route, final HttpRequestWrapper request, final HttpClientContext context, final HttpExecutionAware execAware) throws IOException, HttpException {
//首先判断配置中是否孕育重定向
if (config.isRedirectsEnabled() &&
// 接着将重定向判定权交给 RedirectStrategy 来完成
this.redirectStrategy.isRedirected(currentRequest.getOriginal(), response, context)) {
//还会限制重定向的次数
if (redirectCount >= maxRedirects) {
throw new RedirectException("Maximum redirects ("+ maxRedirects + ") exceeded");
}
redirectCount++;
final HttpRequest redirect = this.redirectStrategy.getRedirect(
currentRequest.getOriginal(), response, context);
}
}
默认策略 org.apache.http.impl.client.DefaultRedirectStrategy。规则很简单,只允许 GET 和 HEAD 请求重定向。
/**
* Redirectable methods.
*/
private static final String[] REDIRECT_METHODS = new String[] { HttpGet.METHOD_NAME, HttpHead.METHOD_NAME };
/**
* @since 4.2
*/
protected boolean isRedirectable(final String method) {
for (final String m: REDIRECT_METHODS) {
if (m.equalsIgnoreCase(method)) {
return true;
}
}
return false;
}
说明: 使用 httpclient-4.5.3
三、okhttp 的重定向规则
尚未跟踪源码,从表现来看,okhttp的重定向归则最为宽松。
总结
遇到调用重定向的问题,如果 postman 调用没问题,可以跟踪一下所使用 http client 的重定向归则。
上一篇: 重定向的那些事
下一篇: windows下安装whl文件