iOS rxSwift: повтор при обновлении токена обновления - PullRequest
0 голосов
/ 08 февраля 2019

У меня есть статическая функция, вызывающая сетевой сервис.Когда происходит ответный код 400, я хотел бы повторить сетевой вызов.

Текущий код работает, за исключением того, что refreshToken в заголовке не обновляется между одной попыткой и другой.

IЯ думаю, что проблема в том, что 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(())
                })
            })
    }

1 Ответ

0 голосов
/ 08 февраля 2019

В статье RxSwift и повторная попытка запроса по сети, несмотря на наличие неверного токена Я объясняю, как сохранить и обновить токен и как обрабатывать повторы при возникновении ошибки 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, чтобы он могполучить токен, даже если у него нет токена обновления.

Ниже приведен пример использования 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) }

В этой статье я объясняю, что представляет собой каждая строка кода.и как это работает.

...