Это моя суть!(Спасибо, что назвали это отлично.) Вы видели статью, которая сопровождала это?https://medium.com/@danielt1263/retrying-a-network-request-despite-having-an-invalid-token-b8b89340d29
В обработке 401 повторных попыток есть два ключевых элемента.Во-первых, вам нужен способ вставить токены в ваши запросы и запустить конвейер запросов с Observable.deferred { tokenAcquisitionService.token.take(1) }
.В вашем случае это означает, что вам нужен URLRequest.init, который будет принимать Ресурс и токен, а не только ресурс.
Во-вторых, выдается ошибка TokenAcquisitionError.unauthorized
, когда вы получаете 401 и заканчиваетеконвейер запроса с .retryWhen { $0.renewToken(with: tokenAcquisitionService) }
Итак, учитывая то, что у вас есть выше, для обработки повторных попыток токена все, что вам нужно сделать, это включить мой TokenAcquisitionService в ваш проект и использовать это:
func getToken(_ oldToken: Token) -> Observable<(response: HTTPURLResponse, data: Data)> {
fatalError("this function needs to be able to request a new token from the server. It has access to the old token if it needs that to request the new one.")
}
func extractToken(_ data: Data) -> Token {
fatalError("This function needs to be able to extract the new token using the data returned from the previous function.")
}
let tokenAcquisitionService = TokenAcquisitionService<Token>(initialToken: Token(), getToken: getToken, extractToken: extractToken)
final class Client<R> where R: ResourceType {
let session: URLSession
init(session: URLSession = URLSession.shared) {
self.session = session
}
func request<T>(_ resource: R) -> Single<T> where T: Decodable {
return Observable.deferred { tokenAcquisitionService.token.take(1) }
.map { token in URLRequest(resource: resource, token: token) }
.flatMapLatest { [session] request in session.rx.response(request: request) }
.do(onNext: { response, _ in
if response.statusCode == 401 {
throw TokenAcquisitionError.unauthorized
}
})
.map { (_, data) -> T in
return try JSONDecoder().decode(T.self, from: data)
}
.retryWhen { $0.renewToken(with: tokenAcquisitionService) }
.asSingle()
}
}
Обратите внимание, это может быть случай, когда функция getToken
должна, например, представить контроллер представления, который запрашивает учетные данные пользователя.Это означает, что вам нужно представить свой контроллер представления входа в систему (или UIAlertController) для сбора данных.Или, возможно, при входе в систему вы получаете как токен авторизации, так и токен обновления с вашего сервера.В этом случае TokenAcquisitionService должен удерживать их обоих (т. Е. Его T
должен быть (token: String, refresh: String)
. Любой из них в порядке.
Единственная проблема со службой состоит в том, что если получение нового токена завершается неудачно, вся служба закрывается. Я еще не исправил это.