【问题标题】:iOS rxSwift: retryWhen updating refresh tokeniOS rxSwift:重试更新刷新令牌时
【发布时间】:2019-02-08 17:22:01
【问题描述】:

我有一个调用网络服务的静态函数。 当 400 响应代码发生时,我想重做网络调用。

当前代码可以正常工作,只是标头中的 refreshToken 在一次尝试和另一次尝试之间没有更新。

我认为问题是因为创建了 Observable 但 请求函数 在重试时没有更新。

我在网上说我应该在 Observable 上使用延迟方法,但我不知道如何。

我尝试将代码:headers = [HeaderKeys.refreshToken.rawValue: "test test"] 移动到任何地方,但它仍然从未使用“测试测试”刷新令牌进行调用。它总是使用旧的。

我该如何解决这个问题?

 static func getAccessToken() -> Observable<GetAccessTokenResponse> {
            var retryCounter = 0
        let maxRetryCounter = 3
        let delayRetry = 10.0

        guard let refreshToken = NetworkHelper.shared.refreshToken else {
            return Observable.error(AuthenticationError.networkError)
        }

        var headers = [HeaderKeys.refreshToken.rawValue: refreshToken]


        return NetworkHelper.shared
            .request(url: CoreAPI.accessToken.url, request: nil, headers: headers, responseType: GetAccessTokenResponse.self, method: .get, encoding: nil)
            .catchError({ (error) -> Observable<(GetAccessTokenResponse?, Int)> in
                return Observable.error(AuthenticationError.networkError)
            })
            .flatMap({ (response) -> Observable<GetAccessTokenResponse> in
                // check http status code
                switch response.1 {
                case 200:
                    guard response.0?.accessToken != nil else {
                        return Observable.error(AuthenticationError.genericError)
                    }
                    // success
                    return Observable.just(response.0!)
                case 400:
                    // invalid parameters, refresh token not existing
                    return Observable.error(AuthenticationError.invalidParameters)
                case 404:
                    // user not existing
                    return Observable.error(AuthenticationError.userDoesntExist)
                default:
                    // by default return network error
                    return Observable.error(AuthenticationError.networkError)
                }
            })
            .retryWhen({ (errors) -> Observable<Void> in
                return errors
                    .do(onNext: { (error) in
                        headers = [HeaderKeys.refreshToken.rawValue: "test test"]
                    })
                    .flatMap({error -> Observable<Int> in
                        debugLog("Retrying get refresh token")
                        if retryCounter >= maxRetryCounter {
                            let authError = error as? AuthenticationError ?? .genericError
                            if authError == AuthenticationError.invalidParameters {
                                // publish logged false on subject
                                VDAAuthenticationManager.shared.logged.onNext(false)
                            }
                            return Observable.error(error)
                        }
                        // increase the retry counter and retry
                        retryCounter += 1
                        return Observable<Int>.timer(delayRetry, scheduler: MainScheduler.instance)
                })
                .flatMap ({ (_) -> Observable<Void> in
                    return Observable.just(())
                })
            })
    }

【问题讨论】:

    标签: ios swift rx-swift


    【解决方案1】:

    RxSwift and Retrying a Network Request Despite Having an Invalid Token 文章中,我解释了如何保留和更新令牌,以及在收到 401 错误时如何处理重试。使用 deferred 是答案的一部分。

    在您的特定情况下。看起来你可以像这样使用我的服务:

    func getToken(lastResponse: GetAccessTokenResponse?) -> Observable<(response: HTTPURLResponse, data: Data)> {
        guard let refreshToken = lastResponse?.refreshToken else { return Observable.error(AuthenticationError.networkError) }
        var request = URLRequest(url: CoreAPI.accessToken.url)
        request.addValue(refreshToken, forHTTPHeaderField: HeaderKeys.refreshToken.rawValue)
        return URLSession.shared.rx.response(request: request)
    }
    
    func extractToken(data: Data) throws -> GetAccessTokenResponse {
        return try JSONDecoder().decode(GetAccessTokenResponse.self, from: data)
    }
    
    let tokenService = TokenAcquisitionService(initialToken: nil, getToken: getToken, extractToken: extractToken(data:))
    

    在上面,您必须传递一个有效的 initialToken 而不是nil,否则您必须修改getToken,以便即使它没有刷新令牌也可以获得令牌。

    如何使用延迟的示例如下:

    let response = Observable
        .deferred { tokenAcquisitionService.token.take(1) }
        .flatMap { makeRequest(withToken: $0) }
        .map { response in
            guard response.response.statusCode != 401 else { throw ResponseError.unauthorized }
            return response
        }
        .retryWhen { $0.renewToken(with: tokenAcquisitionService) }
    

    我在文章中解释了每行代码的用途和工作原理。

    【讨论】:

    • 嗨丹尼尔,你的代码和你的文章对我来说不是很清楚。你能帮我用你的解决方案实现我的代码吗?
    • 我在上面给了你一个例子。只需将makeRequest(withToken:) 替换为发出需要令牌的请求的函数。如果令牌错误,请确保您的函数发出 ResponseError.unauthorized
    • 我在获取令牌和提取令牌部分有一些问题...我只想在 400 发生时重做相同的服务调用,并从单例类中获取新的刷新令牌。我正在使用已经获取新访问令牌的服务实现重试,但有时它是并行完成的,我只想从 db 获取新的更新刷新令牌以收集新的访问令牌
    • 我想知道,当两个请求同时发送并且都以 401 结尾的情况下,这是否正确处理?会发送一个或两个刷新请求吗?如果发送了两个刷新请求,则会出现竞争条件。
    • 是的,它可以正确处理多种 401 情况。只需在每个请求的renewToken 中使用相同的 tokenAcquisitionService 对象。只会发出一个新令牌请求,然后在获取新令牌时重试所有失败的网络请求。
    猜你喜欢
    • 1970-01-01
    • 2013-03-28
    • 1970-01-01
    • 1970-01-01
    • 2022-10-31
    • 2022-12-23
    • 2021-08-20
    • 2017-04-14
    • 1970-01-01
    相关资源
    最近更新 更多