Сериализовать RxSwift

Я работаю над своей собственной реализацией OAuth2 с использованием RxSwift. Для аутентификации запроса у меня есть аутентификатор, который извлекает и сохраняет токен доступа. Поэтому, если у меня есть действующий токен доступа, Authenticator вернет его сразу, иначе он выполнит запрос к серверу. Это именно то, что делает accessTokenRequest().

request = authenticator
            .accessTokenRequest()
            .map(toAuthorizationHeader)
            .flatMap {self.actualRequest(withEndpoint: endpoint, authHeader: $0)}

Проблема в том, что когда у меня есть несколько запросов, запрашивающих аутентификацию, и не хранится действительный токен доступа, Authenticator также будет делать запросы с несколькими токенами доступа. Это нежелательно, так как в идеале должен выполняться только первый запрос токена доступа, а все последующие запросы должны ждать, пока аутентификатор завершит выборку токена доступа.

По сути, я хотел бы сериализовать функцию accessTokenRequest(), как критическую область.

Есть идеи, как этого добиться?

Большое спасибо!

ОБНОВЛЕНИЕ

Это упрощенное содержание функции accessTokenRequest()

func accessTokenRequest() -> Observable<OAuth2Credential> {
    if let credential = self.cachedCredential where !credential.isExpired  {

        // All subsequent requests should already enter here and return right away

        return Observable.just(credential)
    }

    ...

    // First request should reach here and request access token from the server.

    return NetworkClient.AccessToken()
            .doOnNext(cacheCredential)
}

person Bruno Morgado    schedule 15.08.2016    source источник
comment
Если вы хотите остановить последовательность, когда ваш токен доступа недействителен, вы должны проверить свой токен в функции accessTokenRequest (). Если токен недействителен - верните Observable.error () и используйте оператор RxSwift catchError ().   -  person Svyatoslav    schedule 16.08.2016
comment
@Svyatoslav Не уверен, что я понимаю, что вы имеете в виду. Я не хочу ошибаться в последовательности. Я просто хочу приостановить последующие запросы до тех пор, пока не завершится запрос токена доступа первого запроса, а затем использовать кэшированные учетные данные вместо того, чтобы делать новые запросы токена доступа. Я обновил вопрос содержанием функции accessTokenRequest ()   -  person Bruno Morgado    schedule 16.08.2016
comment
Я очень хочу вам помочь, но не могу воспроизвести вашу ошибку. Можете ли вы показать мне часть вашего кода, использующего Github, или добавить код? Например, я не понял, почему вы используете .doOnNext (cacheCredential). Какой смысл использовать этот оператор в вашем случае?   -  person Svyatoslav    schedule 16.08.2016


Ответы (1)


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

Есть несколько решений, давайте посмотрим, как этого добиться разными способами.

Без кеширования

Если вам не нужно кэширование, вы можете преобразовать accessTokenRequest() в общий ресурс и воспроизвести наблюдаемое с помощью shareReplay(1). Это означает, что внутренний наблюдаемый будет запускать один запрос, а затем передавать результат всем подпискам или подключенным наблюдаемым объектам, поэтому изменение будет следующим:

func accessTokenRequest() -> Observable<OAuth2Credential> {
    // First request should reach here and request access token from the server.
    return NetworkClient.AccessToken().shareReplay(1)
}

С кешированием

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

person bontoJR    schedule 19.01.2017