調用接口A,若是token過時,那麼就調用能夠刷新token的接口B,從新獲取token。獲取到新的token之後自動調用接口A,完成接口A的請求。 本文用兩種方法實現。json
建立一個MoyaProvider 的子類,重寫init的初始化方法,在初始化方法裏判斷statusCode爲401的時候,去調用刷新token的B接口,B接口獲取到token之後再調用A接口swift
import Moya
import SwiftyJSON
class RefreshbleMoyaProvider<T: TargetType>: MoyaProvider<T>{
// 重寫MoyaProvider的初始化方法,在初始化方法裏作相應的判斷
@discardableResult
open override func request(_ target: T,
callbackQueue: DispatchQueue? = .none,
progress: ProgressBlock? = .none,
completion: @escaping Completion) -> Cancellable {
return super.request(target, callbackQueue: callbackQueue, progress: progress, completion: { response in
print(response,"3")
switch response {
case .success(let result):
do {
// 若是調用原來的接口直接成功,沒有401,沒有token 過時,那就直接回調completion
let res = try result.filterSuccessfulStatusAndRedirectCodes()
print(res,"4")
completion(response)
} catch {
// 若是調用原來的接口返回401,token過時
if result.statusCode >= 400 {
// 那就去刷新token
self.refreshTokenSyncRequest(success: {
// 刷新token成功之後,再次請求以前的接口
self.request(target, callbackQueue: callbackQueue, progress: progress) { (newResponse) in
completion(newResponse)
}
}, failure: {
// 會無限遞歸
// completion(response)
})
}
}
case .failure(_):
// 若是不是401,是請求超時啊,斷網啊之類的錯誤,直接回調completion,不去刷新token
completion(response)
}
})
}
// 刷新token的接口調用方法
func refreshTokenSyncRequest(success: @escaping ()->(),failure: @escaping ()->()) {
// 判斷refreshToken 是否存在
guard let refreshToken = UserDefaults.standard.string(forKey: refresh_token) else{
// 直接登陸
return
}
// 使用moya調用刷新token的接口(這個地方請實現你本身的刷新token的接口。其餘地方都不用動)
refreshProvider.request(.refreshToken(refreshToken: refreshToken)) { (response) in
switch response {
case .success(let result):
print(result,"5")
do {
// 獲取token成功之後,存儲token
let res = try result.filterSuccessfulStatusAndRedirectCodes()
let json = JSON(res.data)
UserDefaults.standard.set(json["data"]["access_token"].string, forKey: access_token)
UserDefaults.standard.set(json["data"]["refresh_token"].string, forKey: refresh_token)
UserDefaults.standard.set(json["data"]["user"].string, forKey: user)
UserDefaults.standard.set(json["data"]["jti"].string, forKey: jti)
UserDefaults.standard.set(json["data"]["token_type"].string, forKey: tokenType)
// 閉包回調成功
success()
} catch {
// 若是刷新token都失敗了。。。那就登陸吧
if result.statusCode == 401 {
// 直接登陸
}
}
case .failure(let err):
// 直接登陸
SVProgressHUD.showError(withStatus: err.localizedDescription)
print(err.localizedDescription,"6")
}
}
}
}
複製代碼
let afterSaleProvider = RefreshbleMoyaProvider<TargetType>()
api
和MoyaProvider的使用方法同樣,只不過使用RefreshbleMoyaProvider來建立,TargetType是你本身建立的那個TargetType。而後就正常使用。bash
使用RxSwift+Moya,利用RxSwift的retryWhen來進行刷新token,而後再請求以前的接口。 主要思路來源於這裏session
須要導入的庫閉包
pod ‘Moya’, ’13.0.0’
pod ‘SwiftyJSON’
pod ‘RxSwift’, ‘~> 4.0’
pod ‘RxCocoa’, ‘~> 4.0’
pod ‘Moya/RxSwift’, ‘~> 13.0’
複製代碼
// 定義一個協議
public protocol SessionProtocol {
// 刷新token的協議
func getTokenRefreshService() -> Single<Response>
// 獲取token失敗的協議
func didFailedToRefreshToken()
// token刷新成功的協議
func tokenDidRefresh (response : JSON)
}
extension PrimitiveSequence where TraitType == SingleTrait, ElementType == Response {
public func refreshAuthenticationTokenIfNeeded(sessionServiceDelegate : SessionProtocol) -> Single<Response> {
return self.retryWhen { responseFromFirstRequest in
responseFromFirstRequest.flatMap { originalRequestResponseError -> PrimitiveSequence<SingleTrait, ElementType> in
if let lucidErrorOfOriginalRequest : MoyaError = originalRequestResponseError as? MoyaError {
// MoyaError是一個枚舉
let moyaError = lucidErrorOfOriginalRequest
switch moyaError {
case .statusCode(let res ):
//拿到Response
let response = res
print(response)
// 判斷是否是401,token過時
if response.statusCode == 401 {
// token 過時,去請求新的token
// sessionServiceDelegate 代理的方法getTokenRefreshService 來獲得刷新token的接口的請求客觀察序列
return sessionServiceDelegate
.getTokenRefreshService()
// 對可觀察序列進行篩選,篩選 statusCode 再200-399之間的請求結果
.filterSuccessfulStatusAndRedirectCodes()
.catchError { tokeRefreshRequestError -> Single<Response> in
// 若是請求新的接口篩選失敗
if let lucidErrorOfTokenRefreshRequest : MoyaError = tokeRefreshRequestError as? MoyaError {
// 這個時候只能從新登陸
sessionServiceDelegate.didFailedToRefreshToken()
// 並返回失敗
return Single.error(lucidErrorOfTokenRefreshRequest)
}
// 返回失敗
return Single.error(tokeRefreshRequestError)
}
.flatMap { tokenRefreshResponse -> Single<Response> in
// 若是請求新的接口篩選成功 那麼刷新token成功
let result = tokenRefreshResponse
let json = JSON(result.data)
// 判斷接口返回中的code是否是成功,若是成功
if json[「code」] == 「000000」 {
if json["data"].exists() {
// 回調成功之後拿到的json,並在sessionServiceDelegate 協議的 tokenDidRefresh 方法中 保存這個新的token
sessionServiceDelegate.tokenDidRefresh(response: json)
//而後從新請求以前的接口
return self.retry(1)
}
}else {
// 若是code不成功,返回錯誤
return Single.error(lucidErrorOfOriginalRequest)
}
// 返回錯誤,由於成功只有 json["code"] == "000000" 的時候
return Single.error(lucidErrorOfOriginalRequest)
}
}
default:
// 其餘不爲401的時候 返回錯誤,
return Single.error(lucidErrorOfOriginalRequest)
}
}
// 若是第一個接口返回的錯誤 沒法解析,那就返回失敗,不須要刷新token
return Single.error(originalRequestResponseError)
}
}
}
}
// MARK: - SwiftyJSON
extension ObservableType where E == Response {
func mapSwiftyJSON(_ failsOnEmptyData: Bool = true) -> Observable<JSON> {
return flatMap { response -> Observable<JSON> in
return Observable.just(try JSON(response.mapJSON(failsOnEmptyData: failsOnEmptyData)))
}
}
}
// MARK: - SwiftyJSON
extension PrimitiveSequence where TraitType == SingleTrait, ElementType == Response {
func mapSwiftyJSON(_ failsOnEmptyData: Bool = true) -> Single<JSON> {
return flatMap { response -> Single<JSON> in
return Single.just(try JSON(response.mapJSON(failsOnEmptyData: failsOnEmptyData)))
}
}
}
複製代碼
初始化一個provider let provider = MoyaProvider <TargetType>()
ide
建立一個結構體,實現協議,經過provider實現接口的調用封裝ui
// 建立一個結構體,遵循SessionProtocol 協議
struct Network: SessionProtocol {
func getTokenRefreshService() -> Single<Response> {
//
guard let refreshToken = UserDefaults.standard.string(forKey: refresh_token) else{
return Single.just(Response.init(statusCode: 401, data: Data()))
}
// return 刷新token的rx方法
return refreshProvider.rx.request(.refreshToken(refreshToken: refreshToken))
}
func didFailedToRefreshToken() {
print(「didFailedToRefreshToken」)
// 刷新token失敗之後的操做,能夠從新登陸
// let vc = getCurrentController()
//
// let loginVC = UIStoryboard(name: "Login", bundle: nil).instantiateViewController(withIdentifier: "LoginVC") as! LoginVC
//
// vc?.present(loginVC, animated: true, completion: nil)
}
func tokenDidRefresh(response: JSON) {
print(「tokenDidRefresh=========\n」,response)
// 刷新成功之後存儲token和 refresh_token
UserDefaults.standard.set(response["data"]["access_token"].string, forKey: access_token)
UserDefaults.standard.set(response["data"]["refresh_token"].string, forKey: refresh_token)
}
// 定義一個request方法,封裝實現接口的調用
func request(
target: YNETCAfterSalesService,
success successCallback: @escaping (JSON) -> Void,
failure failureCallback: @escaping (MoyaError) -> Void) {
// 調用接口 provider 是定義的MoyaProvider
let _ = provider.rx.request(target)
.filterSuccessfulStatusAndRedirectCodes()
.refreshAuthenticationTokenIfNeeded(sessionServiceDelegate: self)
.mapSwiftyJSON(true)
.subscribe { event in
switch event {
case .success(let res) :
successCallback(res)
case .error(let error):
let err = error
failureCallback(err as! MoyaError)
}
}
}
}
複製代碼
Network().request(target: .getList, success: { (json) in
// 此時的json是徹底成功以後的json,直接用來轉爲model,不用再去判斷亂七八糟的成功失敗
print(json)
}, failure: { (err) in
// 此時的失敗就是全部的失敗的狀況
print(err)
})
複製代碼
以上就是實現的moya刷新token的所有接口,有時間會上傳demo.spa