HTTP 重定向原理
HTTP 协议中,Server 端可以通过 HTTP 状态码 + Location 响应头 的方式告知 Client, 当前访问的地址被移除,请访问新的资源 ;如下所示:
- 301 状态码 告知 Client 当前资源被永久移除
- Location 响应头 告知 Client 当前访问的资源被移到了什么地方
可以看到,当前请求还是有响应结果的,Client 可以选择 显示响应结果 ,或者 跳转到新的资源地址
注意 1: 重定向是 针对 URL 资源的,不是针对域名 ;
- 比如:Server 端返回的 Location 值,完全可以是
Location: https://h2.kail.xyz/other-resource
- 即 访问的是
http://h2.kail.xyz/
,之后会跳转到 https://h2.kail.xyz/other-resource
注意 2:一般重定向后的资源会转成 GET 请求,也可以通过其他状态码控制使用原始请求方式,后面会说明
- 假如原始请求是 POST,重定向后的资源会变成 GET 方式访问
HttpClient 中 Redirect 的默认行为
httpclient:4.5.6
我们一般这样创建一个默认的 HttpClient
等同于
在 build()
时构造 CloseableHttpClient
,关于重定向的部分如下:
RedirectStrategy 重定向策略
重试策略使用的默认实现是 DefaultRedirectStrategy
,主要作用就是:
- 判断是否要重定向:响应头中包含
Location
,且 状态码是 301
、302
、303
、307
其中之一,HttpClient5 支持 308
- 获取重定向后的地址: 获取响应头中的
Location
值
- 转换请求类型 ,后面会说明(HttpClient5 转换逻辑放到
RedirectExec
中)
RedirectExec 重定向执行器
其核心逻辑是 根据是否重定向,进行循环请求 ,简化后的伪代码如下:
禁用重定向
重定向默认是开启,如果您需要禁用 HttpClient 的重定向功能,从上面 HttpClients.custom().build()
和 RedirectExec
的伪代码中可以看出,禁用重定向有两种方式:
- 实例级别禁用 :禁止 RedirectExec 的构建,在整个请求逻辑中,没有 Redirect 相关逻辑的代码
- 请求级别禁用: RedirectExec 仍在请求处理链中,但是不进行重定向,可以控制到指定的请求
实例级别禁用
请求级别禁用
重定向状态码的语义
永久 vs 临时
永久: 原始资源被永久移除, 当你发起请求时,应该直接访问重定向后资源 ,客户端会缓存 301 状态,下次直接跳到新的资源地址,不会产生真实的请求
临时: 原始资源可能还在,不确定什么时候恢复, 当你发起请求时,应该先访问原始资源
测试:http://httpbin.org/status/301,第二次会走磁盘缓存
测试:http://httpbin.org/status/302,第二次仍然发起请求
301/302 vs 308/307 状态
301/302
和 308/307
对应的 永久 和 临时 语义是一样的
- ❤
308/307
状态码 不允许 Client 将原本为 非 GET 的请求重定向到 GET 请求上 ,即 会保留原始的请求方式
- ❤ 而
301/302
会把 POST 请求 转为 GET 请求访问 Location
的值
- 测试详见下方:「HttpClient 对 状态码 的处理方式」
303 状态
- 可以理解为,原始请求的资源 和 重定向后的资源 都可以访问,重定向到的资源并不是你所请求的资源,而是对你所请求资源的一些描述
- ❤ 与 302 类似,区别是 302 只会把 POST 转成 GET 方式访问 ,303 除了 GET 和 HEAD,其他都会被转成 GET
HttpClient 对 状态码 的处理方式
不区分永久和临时的语义
HttpClient 并 不区分永久和临时的语义 ,即 每次都会事先访问原始资源,再根据请求结果重定向,相当于每次访问会产生两次请求
这里配置 301 重定向,HTTP 重定向到 HTTPs
测试代码
Nginx 日志,每次调用,两次请求
区分 301、308 状态
测试代码
301
状态时的 Nginx 日志,POST
重定向后变为了 GET
308
状态时的 Nginx 日志,重定向后 仍然是 POST
(405 是因为 Nginx 不允许对静态资源发起 POST 请求)
HttpClient 对请求转换的伪代码
httpclient:5.1.3
支持重定向的状态码 @see DefaultRedirectStrategy
状态码请求方式转换 @see RedirectExec