Обновите токен и повторите запрос, используя RxSwift - PullRequest
1 голос
/ 03 июня 2019

Я получаю от своего бэкэнда accessToken и refreshToken. Когда я получаю ответ с помощью statusCode 401, мне нужно заменить accessToken на refreshToken (accessToken = refreshToken) и повторить запрос. Если запрос не удался в третий раз, мне нужно попросить пользователя снова войти в систему. Я пытаюсь применить эту логику, используя код, описанный ниже, но он не работает. Запрос больше не вызывается. Как я могу это сделать?

// example of usage:
public func login(with parameters: LoginParameters) -> Single<User> {
        return provider.rx.request(MultiTarget(UserTarget.login(parameters))).handleResponse(User.self, using: jsonDecoder)
    }

func handleResponse<D: Decodable>(_ type: D.Type, atKeyPath keyPath: String? = nil, using decoder: JSONDecoder = JSONDecoder(), failsOnEmptyData: Bool = true) -> Single<D> {
        return self.flatMap({ (response) -> Single<D> in

            switch response.statusCode {
            case 200...299:
                RequestsFailedCount.count = 0
                return Single.just(try response.map(type, atKeyPath: keyPath, using: decoder, failsOnEmptyData: failsOnEmptyData))
            case 500:
                return Single.error(try response.map(ResponseError.self))
            default:
                if response.statusCode == 401, let urlResponse = response.response {

                    guard RequestsFailedCount.count <= 3 else {
                        RequestsFailedCount.count = 0
                        return Single.error(InvalidChallengeError())
                    }

                    RequestsFailedCount.count += 1
                    if let challenge = urlResponse.allHeaderFields[HeaderType.authenticate.rawValue] as? String, challenge.contains(self.challenge) {
                        var keychain = KeychainManager()
                        keychain.accessToken = keychain.refreshToken

                        return self.retry(1).map(type, atKeyPath: keyPath, using: decoder, failsOnEmptyData: failsOnEmptyData)
                    } else {
                        return Single.error(try response.map(ResponseError.self))
                    }

                } else {
                    return Single.error(try response.map(ResponseError.self))
                }
            }
        })
    }

1 Ответ

0 голосов
/ 04 июня 2019

Вот статья со ссылкой на код, который должен вам помочь: https://medium.com/@danielt1263/retrying-a-network-request-despite-having-an-invalid-token-b8b89340d29

В результате вам нужно создать службу получения токена, которая будет удерживать текущий токен и вставляться в сеть.Цепочка вызовов для обновления токена при необходимости ...

Он будет использоваться следующим образом:

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) } // gets the current token
        .map { try request($0) } // creates the request using the current token
        .flatMap { response($0) } // gets response from server
        .map { response in
            guard response.response.statusCode != 401 else { throw TokenAcquisitionError.unauthorized }
            return response
        }
        .retryWhen { $0.renewToken(with: tokenAcquisitionService) }
}

Служба tokenAcquisitionService принимает .unauthorized ошибки и пытается получить новый токен с сервера.,Если он успешен, он попытается повторить цепочку.

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

...