在 iOS 中,URLSession 对 HTTP 重定向(3xx 状态码)有非常成熟的默认处理机制。理解这一点对于处理登录跳转、短链接还原或 CDN 调度至关重要。
1. URLSession 的默认行为
当服务器返回 301、302、307 或 308 状态码时,URLSession 的默认行为是:自动跟随(Auto-follow) 。
-
自动跳转:它会自动读取响应头中的
Location字段,并立即发起对新 URL 的请求。 -
次数限制:为了防止死循环(A 跳 B,B 跳 A),系统默认最多跟随 16 次重定向,超过后请求会失败。
-
方法改变:
- 如果收到
301或302,原本的POST请求可能会被浏览器/系统自动降级为GET请求(根据 RFC 规范的历史演变)。 - 如果收到
307或308(严格重定向),请求方法会被保持不变。
- 如果收到
-
Header 传递:默认情况下,敏感的 Header(如
Authorization)在跨域重定向时会被移除以保证安全。
2. 如何自定义重定向行为?
如果你想阻止自动跳转(例如:你想获取短链接跳转后的真实地址,但不想加载那个页面),或者你想在跳转时手动修改 Header,你需要使用 URLSessionTaskDelegate。
实现 willPerformHTTPRedirection 代理方法
每当发生重定向时,URLSession 都会询问代理该如何处理。
Swift
class NetworkDelegate: NSObject, URLSessionTaskDelegate {
func urlSession(_ session: URLSession,
task: URLSessionTask,
willPerformHTTPRedirection response: HTTPURLResponse,
newRequest request: URLRequest,
completionHandler: @escaping (URLRequest?) -> Void) {
// 方案 A:允许默认重定向(直接返回系统准备好的 newRequest)
// completionHandler(request)
// 方案 B:拒绝重定向(返回 nil,请求会停留在当前 3xx 响应)
// completionHandler(nil)
// 方案 C:修改后重定向(例如手动添加 Token)
var modifiedRequest = request
modifiedRequest.setValue("Bearer new_token", forHTTPHeaderField: "Authorization")
completionHandler(modifiedRequest)
print("发现重定向至: (request.url?.absoluteString ?? "")")
}
}
3. 不同场景的选择建议
| 场景 | 推荐做法 |
|---|---|
| 普通网页跳转 | 默认行为即可,无需干预。 |
| 短链接还原 | 拦截并取消。在代理中获取 newRequest.url 后直接 completionHandler(nil)。 |
| 跨域 Token 丢失 | 自定义重定向。在代理中判断 newRequest 的 Host,如果是受信任域名,手动重新注入 Authorization。 |
| 防止无限循环 | 默认行为。系统内置的 16 次限制足以处理绝大多数情况。 |
4. 💡 避坑指南:Async/Await 注意事项
如果你使用最新的 URLSession.shared.data(for:) 这种 async/await 语法,你无法直接在调用处拦截重定向。你必须在初始化 URLSession 时传入自定义的 delegate:
Swift
let delegate = NetworkDelegate()
let session = URLSession(configuration: .default, delegate: delegate, delegateQueue: nil)
// 现在的请求会受 delegate 中的 willPerformHTTPRedirection 控制
let (data, response) = try await session.data(for: request)