Alamofire请求重试机制和更新token

本文介绍了如何使用Alamofire的RequestInterceptor协议来创建自定义的RequestAdapter和RequestRetrier,实现网络请求的自动重试。在401错误时,通过刷新token并重新发送请求,避免手动处理重试逻辑。同时,通过validate()方法确保401状态码被视为错误,从而触发重试机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 应用场景

发送网络请求时,一些请求失败后需要自动重试。大多数情况下,在重试前需要做一些操作,比如更新参数,更新 token 等。
本文对应的真实场景: 发送请求 -> 返回401 -> 更新 token -> 重新请求

如果不知道 Alamofire 提供了 retrier 机制,你很有可能想到在每一个请求的失败返回中手动重试,千万不要。下面详细介绍怎样利用 Alamofire 便捷地实现重试。

2. Alamofire 的 RequestInterceptor

RequestInterceptor 是一个协议,其中包含了 RequestAdapter 和 RequestRetrier 两个协议。

2.1 RequestAdapter

暂且翻译成”适配器“吧。
这个协议只有一个函数:

func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void)

它可以让你在请求真正发起前,做一些预处理。比如给某些特定函数做重定向、添加一些 header,或者阻拦某些请求。
本文真实场景:给一个特定请求添加一个用于身份验证的 token。

2.1 RequestRetrier

翻译成”重试器“吧。
这个协议也只有一个函数:

func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void)

实现这个函数的时候,可以通过执行函数的 completion 闭包来决定是否需要重试。
所以,在这个函数实现中,我们只要去判断出需要重试的条件,然后执行 completion(.retry),一旦请求有错误返回,就会进入这个函数,重试就触发了。

3. 代码示例

// 新建一个自定义Interceptor, 遵从 RequestInterceptor 协议
class GenericRequestInterceptor: RequestInterceptor {
	func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
        var request = urlRequest
        //Just for gatway host
        guard let baseUrl = gatewayBaseUrl, !accessToken.isEmpty else {
            return completion(.success(request))
        }
        if let urlString = urlRequest.url?.absoluteString, urlString.hasPrefix(baseUrl) {
             // set JWT token to request header
            request.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
        }

        completion(.success(request))
    }
    
    func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
        guard let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401 else {
            completion(.doNotRetry)
            return
        }
        
        // 确保只重试一次,否则就无限重试下去了
        guard request.retryCount == 0, let baseUrl = gatewayBaseUrl else { return completion(.doNotRetry) }
        
        // 如果是特定 URL 并且状态码是 401
        if let urlString = request.firstRequest?.url?.absoluteString, urlString.hasPrefix(baseUrl) {
        	// 重新获取 token
            gatewayAuthService?.refreshJWT(createURLRequest: createURLRequestFunc, complete: {[weak self] accessToken in
                if !accessToken.isEmpty {
                	// 保存新的 token,重试的时候在 adapter 中使用
                    self?.accessToken = accessToken
                    // 重试
                    completion(.retry)
                } else {
                    completion(.doNotRetry)
                }
            })
        } else {
            completion(.doNotRetry)
        }
    }
}

然后在初始化 Session 的时候, 把这个自定义 GenericRequestInterceptor 设置给 Session。

sessionManager = Session(configuration: sessionConfig, interceptor: GenericRequestInterceptor())

这样,如果请求失败,就会触发重试。

4. 小心有坑

本文肯定不仅仅是一个基础教程啦,否则就没意思了。
且看!
如果你发起的是一个错误请求,比如 “https://www.balabalabala.com”, 很快会进入 retrier 函数。
但是,如果请求返回诸如 401 的状态码,它是不会进入 retrier 函数滴。为什么呢?
这是 Alamofire 文档里的一句话:

Response Validation
By default, Alamofire treats any completed request to be successful, regardless of the content of the response.

意思是,像 401 这种返回,按照请求成功处理。所以不会触发 Retrier.
那怎么办呢?
validate()
这个东西可以让你在 response 之前,对特定状态码返回错误。就是你可以在这个里面让 401 当做错误返回。

return sessionManager.request(urlRequest).validate({ request, response, data in
            let statusCode = response.statusCode
            if statusCode != 401 {
                return .success(())
            } else {
                return .failure(AFError.responseValidationFailed(reason: .unacceptableStatusCode(code: 401)))
            }
        }) .response(queue: workerQueue, completionHandler: { [weak self] dataResponse in

		}

这样,遇到 401 就可以进入 Retrier 了。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值