You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Alamofire中全局处理错误?解决410状态码重试两次问题

解决Alamofire中410错误全局处理重复触发的问题

针对你遇到的RequestInterceptor的retry方法被调用两次的问题,这里有几个更合适的Alamofire钩子方案,无需在更高层级手动处理:

1. 使用EventMonitor(推荐)

EventMonitor是Alamofire专门用于监听请求生命周期事件的扩展点,每个请求完成时只会触发一次错误回调,完全避免重复执行的问题。你可以实现它的requestDidComplete(_:with:)方法来检测410错误并执行重置操作:

class SessionExpiryMonitor: EventMonitor {
    func requestDidComplete(_ request: Request, with error: Error?) {
        guard let afError = error as? AFError,
              case .responseValidationFailed(.unacceptableStatusCode(410)) = afError else {
            return
        }
        
        // 执行数据重置、UI刷新逻辑
        resetAppUserData()
        triggerLoginRedirect()
    }
    
    private func resetAppUserData() {
        // 这里写重置用户数据的代码,比如清空本地缓存、用户信息
    }
    
    private func triggerLoginRedirect() {
        // 触发UI重置,比如发送通知跳转登录页、重置导航栈
    }
}

// 初始化Session时添加这个Monitor
let expiryMonitor = SessionExpiryMonitor()
let session = Session(eventMonitors: [expiryMonitor])

2. 自定义ResponseSerializer

如果希望在响应序列化阶段处理410错误,可以自定义全局响应序列化器,在序列化过程中检测错误并执行操作:

class ExpiryHandlingJSONSerializer: JSONResponseSerializer {
    override func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Any {
        if let afError = error as? AFError,
           case .responseValidationFailed(.unacceptableStatusCode(410)) = afError {
            // 执行重置操作
            resetAppUserData()
            triggerLoginRedirect()
        }
        
        return try super.serialize(request: request, response: response, data: data, error: error)
    }
    
    private func resetAppUserData() { /* 重置逻辑 */ }
    private func triggerLoginRedirect() { /* UI重置逻辑 */ }
}

// 给Session设置全局序列化器
let session = Session(responseSerializer: ExpiryHandlingJSONSerializer())

注意:如果你的项目用到多种序列化器(比如JSON、Plain Text),需要为每种序列化器单独处理,通用性不如EventMonitor。

3. 调整RetryInterceptor逻辑(如果坚持用Interceptor)

如果还是想用RequestInterceptor,可以通过给Request添加关联对象标记,确保410错误只被处理一次:

private var handledExpiryKey = "HandledExpiryKey"

extension Request {
    var hasHandledExpiry: Bool {
        get { objc_getAssociatedObject(self, &handledExpiryKey) as? Bool ?? false }
        set { objc_setAssociatedObject(self, &handledExpiryKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
    }
}

class ExpiryRetryInterceptor: RequestInterceptor {
    func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
        guard let afError = error as? AFError,
              case .responseValidationFailed(.unacceptableStatusCode(410)) = afError,
              !request.hasHandledExpiry else {
            completion(.doNotRetry)
            return
        }
        
        request.hasHandledExpiry = true
        // 执行重置操作
        resetAppUserData()
        triggerLoginRedirect()
        
        completion(.doNotRetry)
    }
    
    private func resetAppUserData() { /* 重置逻辑 */ }
    private func triggerLoginRedirect() { /* UI重置逻辑 */ }
}

这种方法需要处理关联对象的线程安全问题,相对繁琐,但能解决当前retry方法重复调用的问题。

内容的提问来源于stack exchange,提问作者urSus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 05:53:21