swift - 刷新令牌并使用 RxSwift 重试请求
问题描述
我从我的后端收到一个 accessToken 和一个 refreshToken。当我收到 statusCode 401 的响应时,我需要用 refreshToken (accessToken = refreshToken) 替换 accessToken 并再次尝试请求。如果第三次请求失败,我需要请求用户重新登录。我正在尝试使用下面描述的代码应用此逻辑,但它不起作用。该请求不再被调用。我怎样才能做到这一点?
// 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))
}
}
})
}
解决方案
这是一篇文章,其中包含可以帮助您的代码链接: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 错误并尝试从服务器获取新令牌。如果成功,它将重试链。
请注意,即使您同时有多个请求在进行中,此服务也可以正常工作,并且它包含一个测试工具来证明它可以工作。
推荐阅读
- mysql - 将现有的自动增量 ID 排序的代码可能是什么?
- reactjs - 如何在 React 中的 onblur 事件后检查组件是否已被单击
- java - Maven 无法使用 eclipse 解决 Jacob 依赖关系
- java - 点燃持久化集合:找不到具有紧凑页脚的对象的元数据
- json - 如何在 React.js 中从 json 给出相对本地路径?
- google-chrome - Chrome 扩展程序截取的屏幕截图 - 隐私风险?
- javascript - 使用其他数据javascript将分号分隔的值拆分为多行
- html - 如何将容器放置在具有自身溢出的固定位置?
- sql - 在 SSIS csv 导出中去除日期/时间分隔符
- javascript - 如何将缓动函数应用于此跳波动画