概述
302等 URL 重定向业务场景需要解决的问题:
302 等重定向状态码,如何正确执行跳转逻辑,要求跳转后,依然需要执行 IP 直连逻辑,多次 302,也能覆盖到。
302等 URL 重定向业务场景问题主要集中在 POST 请求上,解决方案的方向大致有几种:
- 将请求方式统一替换为 GET
- 解决 POST 请求时的重定向问题
将 URL 统一替换为 GET,这种方案在客户端这边是成本最低的,如果团队中达成一致是最好的。不过限制也是显而易见的。那么我们就着重讨论下如何解决 POST 请求时的重定向问题。
POST 请求的重定向问题
对于 GET 请求,重定向问题较为简单,我们着重讨论下 POST 请求的重定向问题,看下不同状态码下的响应方式。
下面介绍下重定向的类型以及解释:
重定向的类型 | 对应协议 | 解释 |
---|---|---|
300 Multiple Choices | HTTP 1.0 | 可选重定向,表示客户请求的资源已经被转向到另外的地址了,但是没有说明是否是永久重定向还是临时重定向。 |
301 Moved Permancently | HTTP 1.0 | 永久重定向,同上,但是这个状态会告知客户请求的资源已经永久性的存在在新的重定向的 URL 上。 |
302 Moved Temporarily | HTTP 1.0 | 临时重定向,在 HTTP1.1 中状态描述是 Found,这个和 300 一样,但是说明请求的资源临时被转移到新的 URL 上,在以后可能会再次变动或者此 URL 会正常请求客户的连接。 |
303 See Other | HTTP 1.1 | 类似于 301/302,不同之处在于,如果原来的请求是 POST,Location 头指定的重定向目标文档应该通过 GET 提取(HTTP 1.1新)。 |
304 Not Modified | HTTP 1.0 | 并不真的是重定向 - 它用来响应条件 GET 请求,避免下载已经存在于浏览器缓存中的数据。 |
305 Use Proxy | HTTP 1.0 | 客户请求的文档应该通过 Location 头所指明的代理服务器提取(HTTP 1.1新)。 |
306 | HTTP 1.0 | 已废弃,不再使用 |
307 Temporary Redirect | HTTP 1.1 | 和302(Found)相同。许多浏览器会错误地响应 302 应答进行重定向,即使原来的请求是 POST,即使它实际上只能在 POST 请求的应答是 303 时 才能重定向。由于这个原因,HTTP 1.1新增了 307,以便更加清楚地区分几个状态代码:当出现 303 应答时,浏览器可以跟随重定向的 GET 和 POST 请求;如果是 307 应答,则浏览器只能跟随对 GET 请求的重定向。(HTTP 1.1新) |
因为常见的重定向为 301、302、303 307,所以下面重点说说这几种重定向的处理方法。
HTTP1.0 文档中的 302(或301) 状态码,原则上是要被废弃的,它在 HTTP1.1 被细分为了 303 和 307。不过 303 和 307 应用并不广泛,现在很多公司对 302(或301) 处理实际上是 303。
总结起来就是:
协议 | 状态码 | 协议规定 | 实际情况 |
---|---|---|---|
HTTP1.0 | 302(或301) | 不建议使用 | 仍在大面积使用 |
HTTP1.1 | 303 + 307 | 旧有302(或301)被细分,并建议使用的新的状态码 | 应用面积较小 |
这些新旧协议的主要差别集中在 POST 请求的重定向处理上:
对于301、302的location中包含的重定向url,如果请求method不是GET或者HEAD,那么浏览器是禁止自动重定向的,除非得到用户的确认,因为POST、PUT等请求是非冥等的(也就是再次请求时服务器的资源可能已经发生了变化)
另外注意307这种情况,表示的是 POST 不自动重定向为 GET ,需要询问访问当前 URL 的用户,是否需要重定向,进行手动重定向。
目前浏览器大都还把302当作303处理了(注意,303是HTTP1.1才加进来的,其实从HTTP1.0进化到HTTP1.1,浏览器什么都没动),它们获取到 HTTP 响应报文头部的 Location 字段信息,并发起一个 GET 请求。
我们可以根据业务需要,对不同的状态码做处理,比如可以对303状态码做如下处理,
- location 中包含重定向 URL 就重定向
- 如果是 POST 请求修改为 GET 请求,并清空 body。
- 清空 host 信息
- 重新发送网络请求
代码示例:
NSString *location = self.response.headerFields[@"Location"];
if (location && location.length > 0) {
NSURL *url = [[NSURL alloc] initWithString:location];
NSMutableURLRequest *mRequest = [self.swizzleRequest mutableCopy];
mRequest.URL = url;
if ([[self.swizzleRequest.HTTPMethod lowercaseString] isEqualToString:@"post"]) {
// POST重定向为GET
mRequest.HTTPMethod = @"GET";
mRequest.HTTPBody = nil;
}
[mRequest setValue:nil forHTTPHeaderField:@"host"];
Cookie 场景重定向问题
之前提到的 Cookie 方案无法解决iOS11之前系统的 302 请求的 Cookie 问题,比如,第一个请求是 http://www.a.com ,我们通过在 request header 里带上 Cookie 解决该请求的 Cookie 问题,接着页面302跳转到 http://www.b.com ,这个时候 http://www.b.com 这个请求就可能因为没有携带 cookie 而无法访问。当然,由于每一次页面跳转前都会调用回调函数:
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
可以在该回调函数里拦截302请求,copy request,在 request header 中带上 cookie 并重新 loadRequest。不过这种方法依然解决不了页面 iframe 跨域请求的 Cookie 问题,毕竟-[WKWebView loadRequest:]只适合加载 mainFrame 请求。
如果通过之前篇幅里提到的 iOS11 的新 API 进行处理,也就不会有该问题。
相关的文章:
补充说明:
概念 | 解释 | 举例 |
---|---|---|
host | 可以是 IP 也可以是 FQDN。 | www.xxx.com 或 1.1.1.1 |
FQDN | fully qualified domain name,由主机名和域名两部分组成 | www.xxx.com |
域名 | 域名分为全称和简称,全称就是FQDN、简称就是 FQDN 不包括主机名的部分 | 比如:xxx.com ,也就是www.xxx.com 这个 FQDN 中,www 是主机名,xxx.com 是域名。 |
文中部分提到的域名,如果没有特殊说明均指的是 FQDN。