问题描述
现在,我正在Swift 4中开发一个iOS应用程序.在这里,我正在使用Alamofire集成API调用.我需要集成正确的方法来自动刷新身份验证令牌并重试以前的API调用.成功登录后,我将存储身份验证令牌.因此,登录后,在每个API中,我都将令牌附加在标题部分中.当令牌过期时,我将得到401.那时候,我需要自动刷新身份验证令牌并再次调用相同的API.我怎样才能做到这一点?我检查了Stackoverflow,但没有任何解决方案.
now I'm working on an iOS application in Swift 4. Here I'm using Alamofire to integrate the API calls. I need to integrate the right way to auto-refresh the authentication token and retry the previous API calls. I'm storing the authentication token once I logged in successfully. So after login, in each API, I'm appending the token in the header part. And when if the token is expired I will get 401. That time I need to auto-refresh the authentication token and recall the same API again. How can I do that? I checked in the Stackoverflow, but I didn't get any solution.
这是我的API调用,
import Foundation
import Alamofire
import SwiftyJSON
class LoveltyAPI {
let loveltyURL = Bundle.main.object(forInfoDictionaryKey: "APIUrlString") as! String // Main URL
let buildVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String //infoDictionary?["CFBundleShortVersionString"] as AnyObject
weak var delegate:LoveltyProtocol?
func get_profile(app_user_id:String, token:String) {
let urlString = "\(loveltyURL)\(get_profile_string)?app_user_id=\(app_user_id)"
let headers = ["Content-Type":"application/json","X-Requested-With":"XMLHttpRequest", "Authentication":"Token \(token)"]
Alamofire.request(urlString, method: .get, encoding: JSONEncoding.default, headers: headers).responseJSON { response in
switch response.result {
case .success:
let swiftyJsonVar = JSON(response.result.value!)
switch response.response?.statusCode {
case 200, 201:
self.delegate?.getUserProfile!(response: swiftyJsonVar["data"].dictionaryObject as AnyObject)
case 401:
self.delegate?.tokenExpired(response: tokenExpired as AnyObject)
case 404:
self.delegate?.serviceError!(response: swiftyJsonVar["message"] as AnyObject)
case 422:
self.delegate?.serviceError!(response: swiftyJsonVar["error"] as AnyObject)
case 503:
self.delegate?.appDisabled(response: swiftyJsonVar.dictionaryObject as AnyObject)
default:
self.delegate?.serviceError!(response: self.serverError as AnyObject)
}
case .failure(let error):
self.delegate?.serviceError!(response: self.serverError as AnyObject)
}
}
}
}
请帮助我.如果您能用我的代码解释一下,那就太好了.
Please help me. If you can explain with my code, it would be very nice.
推荐答案
您需要Alamofire RequestRetrier和RequestAdapter检查此处
You need Alamofire RequestRetrier and RequestAdapter check here
这是我的一些例子:
import UIKit
import Alamofire
class MyRequestAdapter: RequestAdapter, RequestRetrier {
private typealias RefreshCompletion = (_ succeeded: Bool, _ accessToken: String?) -> Void
private let lock = NSLock()
private var isRefreshing = false
private var requestsToRetry: [RequestRetryCompletion] = []
var accessToken:String? = nil
var refreshToken:String? = nil
static let shared = MyRequestAdapter()
private init(){
let sessionManager = Alamofire.SessionManager.default
sessionManager.adapter = self
sessionManager.retrier = self
}
func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
var urlRequest = urlRequest
if let urlString = urlRequest.url?.absoluteString, urlString.hasPrefix(BASE_URL), !urlString.hasSuffix("/renew") {
if let token = accessToken {
urlRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
}
}
return urlRequest
}
// MARK: - RequestRetrier
func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) {
lock.lock() ; defer { lock.unlock() }
if let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401 {
requestsToRetry.append(completion)
if !isRefreshing {
refreshTokens { [weak self] succeeded, accessToken in
guard let strongSelf = self else { return }
strongSelf.lock.lock() ; defer { strongSelf.lock.unlock() }
if let accessToken = accessToken {
strongSelf.accessToken = accessToken
}
strongSelf.requestsToRetry.forEach { $0(succeeded, 0.0) }
strongSelf.requestsToRetry.removeAll()
}
}
} else {
completion(false, 0.0)
}
}
// MARK: - Private - Refresh Tokens
private func refreshTokens(completion: @escaping RefreshCompletion) {
guard !isRefreshing else { return }
isRefreshing = true
let urlString = "\(BASE_URL)token/renew"
Alamofire.request(urlString, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: ["Authorization":"Bearer \(refreshToken!)"]).responseJSON { [weak self] response in
guard let strongSelf = self else { return }
if
let json = response.result.value as? [String: Any],
let accessToken = json["accessToken"] as? String
{
completion(true, accessToken)
} else {
completion(false, nil)
}
strongSelf.isRefreshing = false
}
}
}
我的示例有点复杂,但是是的,总的来说,我们有两个重要的方法,第一个是 adapt(_ urlRequest:URLRequest)throws->URLRequest
(我们在其中附加令牌)在这里,我具有自定义逻辑,其中服务之一不应将此令牌作为标头附加.第二种方法是 func should(_ manager:SessionManager,重试请求:Request,有错误:Error,完成:@escaping RequestRetryCompletion)
,在其中检查错误代码是什么(在我的示例401中).然后我用
My example is a little bit complex, but yes in general we have two important methods first one is adapt(_ urlRequest: URLRequest) throws -> URLRequest
where we attaching the token, here I have custom logic where one of the services have should not attach this token as a header. The second method is func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion)
where I check what is the error code(in my example 401). And then I refresh my tokens with
private func refreshTokens(completion: @escaping RefreshCompletion)
在我的情况下,我具有刷新令牌和访问令牌,并且当我使用刷新令牌调用服务时,我不应该在标头中附加旧的访问令牌.我认为这不是最佳做法,但它是从我不知道的peopele中实施的.
In my case, I have refresh token and access token and when I call the service with refresh token I should not append my old access token in the header. I think this is not best practice but it was implemented from peopele that I don't know.
这篇关于Alamofire自动刷新令牌并重试iOS Swift 4中的上一个API调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!