RxSwift递归Observable实现:令牌过期后重发请求方案求助
我来帮你搞定这个RxSwift里的令牌刷新重试问题!你当前的代码主要有两个问题:一是没有把刷新令牌后重新请求的结果正确传递给最初的观察者,二是嵌套订阅会导致订阅管理混乱、内存泄漏风险。RxSwift提供了专门的操作符来优雅处理这种"失败后重试依赖操作"的场景,下面给你两种靠谱的实现思路:
思路一:用catchError捕获令牌过期错误,刷新后重发请求
首先我们先定义一个自定义错误,把"令牌过期"作为明确的错误类型抛出,这样Rx的错误流能更好地识别和处理:
enum APIError: Error { case tokenExpired case invalidJSON case underlying(Error) }
然后改造你的请求函数,把令牌过期的情况转为APIError.tokenExpired抛出,再用catchError捕获这个错误,先刷新令牌再重新发起原请求:
func apiRequest(_ urlConvertible: URLRequestConvertible) -> Observable<[String: AnyObject]> { return Observable.create { observer -> Disposable in let request = Alamofire.request(urlConvertible).responseJSON { response in switch response.result { case .success(let value): // 优先从服务器响应判断令牌是否过期(比如401状态码或返回的错误标识) if response.response?.statusCode == 401 || self.isTokenExpired() { observer.onError(APIError.tokenExpired) } else { guard let json = value as? [String: AnyObject] else { observer.onError(APIError.invalidJSON) return } observer.onNext(json) observer.onCompleted() } case .failure(let error): observer.onError(APIError.underlying(error)) } } // 记得取消请求的逻辑,避免内存泄漏 return Disposables.create { request.cancel() } } .catchError { error -> Observable<[String: AnyObject]> in // 只处理令牌过期的错误,其他错误直接抛出 guard case APIError.tokenExpired = error else { return Observable.error(error) } // 刷新令牌成功后,重新发起原请求 return self.generateToken() .flatMap { _ in self.apiRequest(urlConvertible) } } }
思路二:用retryWhen实现带次数限制的重试逻辑
如果需要限制令牌刷新的重试次数(比如最多重试2次,避免无限循环),retryWhen会更合适。它能监听错误流,在满足条件时触发原请求的重试:
func apiRequest(_ urlConvertible: URLRequestConvertible) -> Observable<[String: AnyObject]> { return Observable.create { observer -> Disposable in // 这里的请求逻辑和上面一致,抛出对应的APIError let request = Alamofire.request(urlConvertible).responseJSON { response in switch response.result { case .success(let value): if response.response?.statusCode == 401 || self.isTokenExpired() { observer.onError(APIError.tokenExpired) } else { guard let json = value as? [String: AnyObject] else { observer.onError(APIError.invalidJSON) return } observer.onNext(json) observer.onCompleted() } case .failure(let error): observer.onError(APIError.underlying(error)) } } return Disposables.create { request.cancel() } } .retryWhen { errorObservable -> Observable<Void> in return errorObservable.flatMapWithIndex { error, retryIndex -> Observable<Void> in // 只处理令牌过期错误,且重试次数不超过2次 guard case APIError.tokenExpired = error, retryIndex < 2 else { return Observable.error(error) } // 刷新令牌后返回Void,触发原请求重试 return self.generateToken().map { _ in () } } } }
额外注意事项
- 避免重复刷新令牌:如果多个请求同时触发令牌过期,要确保
generateToken()只会发起一次请求。可以给generateToken()加上share(replay: 1, scope: .whileConnected),让多个订阅共享同一个请求结果:func generateToken() -> Observable<Token> { return Observable.create { observer -> Disposable in // 你的令牌请求逻辑 } .share(replay: 1, scope: .whileConnected) } - 优先依赖服务器响应判断过期:尽量用API返回的状态码(比如401)来判断令牌过期,而不是本地的时间判断,避免本地和服务器时间不同步导致误判。
内容的提问来源于stack exchange,提问作者Karthik
相关产品推荐
相关产品推荐

