gpt4 book ai didi

swift - 如何保证在使用 AlamoFire 的 RequestInterceptor 类时一次只运行一次重试?

转载 作者:行者123 更新时间:2023-12-04 12:18:54 25 4
gpt4 key购买 nike

我正在实现一种使用带有 AlamoFire5 的 OAuth2 刷新 session token 的方法,并且我正在尝试找出如何解决这种情况:

1 - When some request fail a refreshToken request must start, thatmust be the only one refreshToken request running at a time. i.e. theother requests that failed should not be retried until that requestfinishes.

2 - If the refreshToken finishes with an error the app must restartsand all the other request that were waiting must be cancelled.

3 - If the the refreshToken request succeeds the token must be updatedand all the other requests waiting must now continue.


我正在使用 AlamoFire 的 RequestInterceptor 类来尝试解决这个问题,到目前为止我的实现是这样的:
final class RequestInterceptor: Alamofire.RequestInterceptor {

private let disposeBag = DisposeBag()
private let lock = NSRecursiveLock()

private var refreshTokenParameters: TokenParameters {
TokenParameters(clientId: "pdappclient",
grantType: "refresh_token",
refreshToken: KeychainManager.shared.refreshToken)
}

private let storage: AccessTokenStorage

init(storage: AccessTokenStorage) {
self.storage = storage
}

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

urlRequest.setValue("Bearer " + storage.accessToken, forHTTPHeaderField: "Authorization")

completion(.success(urlRequest))
}

func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
lock.lock()
defer { lock.unlock() }

guard let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401 else {
return completion(.doNotRetryWithError(error))
}

let refreshTokenRequest: Single<TokenResponse> = NetworkManager.shared
.fetchData(fromApi: IdentityServerAPI.token(parameters: self.refreshTokenParameters))

refreshTokenRequest.subscribe(onSuccess: { token in
self.lock.unlock()
self.storage.accessToken = token.accessToken ?? ""
completion(.retry)
}, onError: { error in
self.lock.unlock()
completion(.doNotRetryWithError(error))
}).disposed(by: disposeBag)
}
}
如何使用 RequestInterceptor 解决这种情况?

最佳答案

您可以使用数组来存储重试闭包,用于在 token 刷新完成之前可能发生的请求,并使用 bool 值来了解正在进行的刷新操作。
你最终会得到这样的结果:

final class RequestInterceptor: Alamofire.RequestInterceptor {

private let disposeBag = DisposeBag()
private let lock = NSRecursiveLock()

private var refreshTokenParameters: TokenParameters {
TokenParameters(
clientId: "pdappclient",
grantType: "refresh_token",
refreshToken: KeychainManager.shared.refreshToken
)
}

private let storage: AccessTokenStorage

private var retryQueue = [(RetryResult) -> Void]()
private var isTokenRefreshing = false

init(storage: AccessTokenStorage) {
self.storage = storage
}

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

urlRequest.setValue("Bearer " + storage.accessToken, forHTTPHeaderField: "Authorization")

completion(.success(urlRequest))
}

func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
lock.lock()
defer { lock.unlock() }

guard let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401 else {
completion(.doNotRetryWithError(error))
return
}
retryQueue.append(completion)

if !isTokenRefreshing {
isTokenRefreshing = true

let refreshTokenRequest: Single<TokenResponse> = NetworkManager.shared
.fetchData(fromApi: IdentityServerAPI.token(parameters: self.refreshTokenParameters))

refreshTokenRequest.subscribe(onSuccess: { token in
self.lock.lock()
defer { self.lock.unlock() }

self.storage.accessToken = token.accessToken ?? ""

self.retryQueue.forEach { $0(.retry) }
self.retryQueue.removeAll()

self.isTokenRefreshing = false
}, onError: { error in
self.lock.lock()
defer { self.lock.unlock() }

self.retryQueue.forEach { $0(.doNotRetryWithError(error)) }
self.retryQueue.removeAll()

self.isTokenRefreshing = false
}).disposed(by: disposeBag)
}
}
}
请注意,如 defer文件指出:

A defer statement is used for executing code just before transferringprogram control outside of the scope that the defer statement appearsin.


所以,先封 defer语句将在 onSuccess 之前执行或 onError关闭。
这就是为什么我们需要再次锁定 onSuccess 内的源和 onError关闭。

关于swift - 如何保证在使用 AlamoFire 的 RequestInterceptor 类时一次只运行一次重试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64173789/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com