简体   繁体   English

iOS rxSwift:重试更新刷新令牌时

[英]iOS rxSwift: retryWhen updating refresh token

I have a static function calling a network service. 我有一个静态函数调用网络服务。 When the 400 response code happens I would like to redo the network call. 当400响应代码发生时,我想重做网络呼叫。

The current code is working, except that the refreshToken in the header does not update between one try and another. 当前代码正常工作,只是头文件中的refreshToken在一次尝试之间不会更新。

I think that the problem is because the Observable created but the request function does not update at the retry. 我认为问题是因为创建了Observable,但是请求函数在重试时未更新。

I rode on the web that I should use a deferred method on the Observable, but I don't know how. 我骑车上网,应该在Observable上使用延迟的方法,但是我不知道怎么做。

I've tried moving the code: headers = [HeaderKeys.refreshToken.rawValue: "test test"] anywhere but still it never makes a call with the "test test" refresh token. 我试过将代码移动到: headers = [HeaderKeys.refreshToken.rawValue: "test test"]任何地方,但它始终不会使用“ test test”刷新令牌进行调用。 it always uses the old one. 它总是使用旧的。

How can I fix this? 我怎样才能解决这个问题?

 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(())
                })
            })
    }

In the article RxSwift and Retrying a Network Request Despite Having an Invalid Token I explain how to keep and update a token and how to handle retries when you get a 401 error. RxSwift中,尽管令牌无效,但重试网络请求一文中,我解释了当出现401错误时如何保留和更新令牌以及如何处理重试。 Using deferred is part of the answer. 使用延迟是答案的一部分。

In your particular case. 在您的特定情况下。 It looks like you could use my service like this: 看来您可以像这样使用我的服务:

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:))

In the above, you will have to pass a valid initialToken instead of nil or you will have to modify the getToken so it can get a token even if it doesn't have a refresh token. 在上面的代码中,您将必须传递一个有效的initialToken而不是nil或者必须修改getToken以便即使它没有刷新令牌也可以获取令牌。

An example of how to use deferred is below: 下面是一个如何使用deferred的示例:

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) }

I explain in the article what each line of code is for and how it works. 我在文章中解释了每一行代码的用途以及工作方式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM