Alamofire/RxSwift 如何在状态码 401 上自动刷新令牌和重试请求
Posted
技术标签:
【中文标题】Alamofire/RxSwift 如何在状态码 401 上自动刷新令牌和重试请求【英文标题】:Alamofire/RxSwift how to refresh token and retry requests automatically on status code 401 【发布时间】:2021-12-14 08:19:33 【问题描述】:在我获得任何请求的第一个 401 状态代码后,我需要自动重试请求的帮助。我正在使用 RxSwift 和 Alamofire,所以调用看起来像这样:
public func getSomeEndpointInfo() -> Observable<PKKInfo>
return Observable.create( observer in
let request = AF.request(Router.info)
request
.responseDecodable(of: Info.self) response in
print("response: \(response)")
if response.response?.statusCode == 401
observer.onError(NetworkError.unauthorized)
guard let decodedItems = response.value else
observer.onError(NetworkError.invalidJSON)
return
observer.onNext(decodedItems)
observer.onCompleted()
return Disposables.create()
)
现在在某些服务中,我有以下代码:
service.getSomeEndpointInfo()
.observe(on: MainScheduler.instance)
.subscribe [unowned self] info in
self._state.accept(.loaded)
onError: [unowned self] error in
print("---> Error")
self.sessionManager
.renewToken()
.observe(on: MainScheduler.instance)
.subscribe token in
print("---> recieved new token")
self.service.getSomeEndpointInfo()
onError: error in
print("---> error generating token")
.disposed(by: self.disposeBag)
.disposed(by: disposeBag)
使用此代码可以工作,但我必须在每个请求上调用更新令牌并将其嵌入到错误订阅中,这感觉不太好。如果您有其他建议,我会在 401 上以某种方式重试请求并在此之前触发更新令牌,我将不胜感激。
【问题讨论】:
【参考方案1】:我写了一篇关于如何做到这一点的文章。 RxSwift and Handling Invalid Tokens.
本文附带完整的代码和测试证明功能。关键是这个答案底部的类。
你可以这样使用它:
typealias Response = (URLRequest) -> Observable<(response: HTTPURLResponse, data: Data)>
func getData<T>(response: @escaping Response, tokenAcquisitionService: TokenAcquisitionService<T>, request: @escaping (T) throws -> URLRequest) -> Observable<(response: HTTPURLResponse, data: Data)>
return Observable
.deferred tokenAcquisitionService.token.take(1)
.map try request($0)
.flatMap response($0)
.map response in
guard response.response.statusCode != 401 else throw TokenAcquisitionError.unauthorized
return response
.retryWhen $0.renewToken(with: tokenAcquisitionService)
您可以使用柯里化来制作共享服务的函数...
func makeRequest(builder: @escaping (MyTokenType) -> URLRequest) -> Observable<(response: HTTPURLResponse, data: Data)>
getData(
response: URLSession.shared.rx.response(request: $0) /* or however Moya makes network requests */ ,
tokenAcquisitionService: TokenAcquisitionService<MyTokenType>(
initialToken: getSavedToken(),
getToken: makeRenewTokenRequest(oldToken:),
extractToken: extractTokenFromData(_:)),
request: builder)
在代码中任何需要更新令牌的地方使用上述函数。
这里是上面使用的TokenAquisitionService。让您的所有请求都使用相同的服务对象。
public final class TokenAcquisitionService<T>
/// responds with the current token immediatly and emits a new token whenver a new one is aquired. You can, for example, subscribe to it in order to save the token as it's updated.
public var token: Observable<T> get
public typealias GetToken = (T) -> Observable<(response: HTTPURLResponse, data: Data)>
/// Creates a `TokenAcquisitionService` object that will store the most recent authorization token acquired and will acquire new ones as needed.
///
/// - Parameters:
/// - initialToken: The token the service should start with. Provide a token from storage or an empty string (object represting a missing token) if one has not been aquired yet.
/// - getToken: A function responsable for aquiring new tokens when needed.
/// - extractToken: A function that can extract a token from the data returned by `getToken`.
public init(initialToken: T, getToken: @escaping GetToken, extractToken: @escaping (Data) throws -> T)
/// Allows the token to be set imperativly if necessary.
/// - Parameter token: The new token the service should use. It will immediatly be emitted to any subscribers to the service.
public func setToken(_ token: T)
extension ObservableConvertibleType where Element == Error
/// Monitors self for `.unauthorized` error events and passes all other errors on. When an `.unauthorized` error is seen, the `service` will get a new token and emit a signal that it's safe to retry the request.
///
/// - Parameter service: A `TokenAcquisitionService` object that is being used to store the auth token for the request.
/// - Returns: A trigger that will emit when it's safe to retry the request.
public func renewToken<T>(with service: TokenAcquisitionService<T>) -> Observable<Void>
【讨论】:
以上是关于Alamofire/RxSwift 如何在状态码 401 上自动刷新令牌和重试请求的主要内容,如果未能解决你的问题,请参考以下文章