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

基于Alamofire/RxSwift实现401状态下自动刷新token并重试请求方案咨询

实现方案

核心思路是将401拦截、token刷新、请求重试逻辑统一封装到网络请求底层,业务侧无需做任何适配,完全消除重复代码。

第一步:全局刷新状态管控

首先对SessionManager的刷新逻辑做防抖处理,避免多个请求同时返回401时重复触发token刷新:

extension SessionManager {
    // 单例共享,管控刷新状态
    static let shared = SessionManager()
    // 标记当前是否正在刷新token
    private let isRefreshing = BehaviorSubject<Bool>(value: false)
    // 刷新成功的回调通知
    private let refreshSuccess = PublishSubject<Void>()
    
    func renewToken() -> Observable<Token> {
        // 如果正在刷新,直接等待刷新成功的通知
        if try! isRefreshing.value() {
            return refreshSuccess
                .flatMap { _ -> Observable<Token> in
                    // 刷新完成后返回最新的token
                    return .just(UserDefaults.standard.currentToken!)
                }
                .take(1)
        }
        
        // 没有正在刷新的请求,触发刷新
        isRefreshing.onNext(true)
        return Observable.create { observer in
            // 调用刷新接口
            let request = AF.request(Router.renewToken)
                .responseDecodable(of: Token.self) { [weak self] response in
                    guard let self = self else { return }
                    self.isRefreshing.onNext(false)
                    switch response.result {
                    case .success(let token):
                        // 保存新token到本地,后续请求自动携带
                        UserDefaults.standard.currentToken = token
                        self.refreshSuccess.onNext(())
                        observer.onNext(token)
                        observer.onCompleted()
                    case .failure(let error):
                        // 刷新失败,抛出错误,可统一触发登出逻辑
                        observer.onError(error)
                        NotificationCenter.default.post(name: NSNotification.Name("UserShouldLogout"), object: nil)
                    }
                }
            return Disposables.create {
                request.cancel()
            }
        }
    }
}

第二步:封装通用网络请求方法,内置401重试逻辑

将原来每个接口单独写的请求逻辑抽成通用方法,内置401重试逻辑,所有业务接口直接复用:

// 网络请求基础封装
func request<T: Decodable>(router: Router, type: T.Type) -> Observable<T> {
    return Observable.create { observer in
        let request = AF.request(router)
            .responseDecodable(of: T.self) { response in
                // 处理401状态码
                if response.response?.statusCode == 401 {
                    observer.onError(NetworkError.unauthorized)
                    return
                }
                // 处理其他错误
                guard let value = response.value else {
                    observer.onError(NetworkError.invalidJSON)
                    return
                }
                observer.onNext(value)
                observer.onCompleted()
            }
        return Disposables.create {
            request.cancel()
        }
    }
    // 核心:401错误时触发重试逻辑
    .retry { errorObservable in
        errorObservable.flatMap { error -> Observable<Void> in
            // 只有401错误才触发重试
            guard let networkError = error as? NetworkError, 
                  networkError == .unauthorized else {
                return .error(error)
            }
            // 调用刷新token逻辑,刷新成功后触发重试
            return SessionManager.shared.renewToken()
                .map { _ in () }
                // 只重试1次,避免无限循环
                .take(1)
        }
    }
}

第三步:业务接口简化实现

原来的业务接口直接调用通用请求方法即可,无需额外处理:

public func getSomeEndpointInfo() -> Observable<PKKInfo> {
    return request(router: Router.info, type: PKKInfo.self)
}

第四步:业务侧调用无需额外处理

业务侧完全不用再处理401错误和刷新逻辑,正常订阅即可:

service.getSomeEndpointInfo()
    .observe(on: MainScheduler.instance)
    .subscribe { [unowned self] info in
        self._state.accept(.loaded)
    } onError: { error in
        // 只有非401错误、或者刷新token失败的错误才会走到这里
        print("请求出错:\(error)")
    }.disposed(by: disposeBag)

额外优化点

  • 可以给retry操作符增加最大重试次数限制,避免刷新成功后又返回401的无限循环问题
  • 刷新token失败时统一触发登出逻辑,无需在每个业务页面单独处理
  • 若需要区分不需要自动重试的接口,可以给通用request方法增加autoRetry401参数,默认开启即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 05:15:03