iOS端302等重定向业务场景 " IP直连 "方案说明

重要

当前最佳实践文档只针对结合使用时,如何使用HTTPDNS解析出的IP,关于HTTPDNS本身的解析服务,请先查看iOS SDK 开发手册

背景信息

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"];

相关参考