我的问题与这个非常相似,但对于 Alamofire:AFNetworking: Handle error globally and repeat request
如何能够在全局范围内捕获错误(通常是 401)并在发出其他请求之前处理它(如果不加以管理,最终会失败)?
我曾考虑链接一个自定义响应处理程序,但在应用的每个请求上都这样做很愚蠢。
也许是子类化,但我应该子类化哪个类来处理它?</p>
最佳答案
考虑到 NSURLSession 的并行性质,在 oauth 流程中处理 401 响应的刷新是相当复杂的。我花了相当多的时间来构建一个对我们来说非常有效的内部解决方案。以下是对其实现方式的总体思路的高度概括。
import Foundation
import Alamofire
public class AuthorizationManager: Manager {
public typealias NetworkSuccessHandler = (AnyObject?) -> Void
public typealias NetworkFailureHandler = (NSHTTPURLResponse?, AnyObject?, NSError) -> Void
private typealias CachedTask = (NSHTTPURLResponse?, AnyObject?, NSError?) -> Void
private var cachedTasks = Array<CachedTask>()
private var isRefreshing = false
public func startRequest(
method method: Alamofire.Method,
URLString: URLStringConvertible,
parameters: [String: AnyObject]?,
encoding: ParameterEncoding,
success: NetworkSuccessHandler?,
failure: NetworkFailureHandler?) -> Request?
{
let cachedTask: CachedTask = { [weak self] URLResponse, data, error in
guard let strongSelf = self else { return }
if let error = error {
failure?(URLResponse, data, error)
} else {
strongSelf.startRequest(
method: method,
URLString: URLString,
parameters: parameters,
encoding: encoding,
success: success,
failure: failure
)
}
}
if self.isRefreshing {
self.cachedTasks.append(cachedTask)
return nil
}
// Append your auth tokens here to your parameters
let request = self.request(method, URLString, parameters: parameters, encoding: encoding)
request.response { [weak self] request, response, data, error in
guard let strongSelf = self else { return }
if let response = response where response.statusCode == 401 {
strongSelf.cachedTasks.append(cachedTask)
strongSelf.refreshTokens()
return
}
if let error = error {
failure?(response, data, error)
} else {
success?(data)
}
}
return request
}
func refreshTokens() {
self.isRefreshing = true
// Make the refresh call and run the following in the success closure to restart the cached tasks
let cachedTaskCopy = self.cachedTasks
self.cachedTasks.removeAll()
cachedTaskCopy.map { $0(nil, nil, nil) }
self.isRefreshing = false
}
}
这里要记住的最重要的事情是,您不想为每个返回的 401 运行刷新调用。大量请求可以同时竞速。因此,您想对第一个 401 采取行动,并将所有其他请求排队,直到 401 成功。我上面概述的解决方案正是这样做的。任何通过 startRequest
方法启动的数据任务都会在遇到 401 时自动刷新。
在此非常简化的示例中未考虑的其他一些需要注意的重要事项是:
- 线程安全
- 保证成功或失败的关闭调用
- 存储和获取 oauth token
- 解析响应
- 将解析后的响应转换为适当的类型(泛型)
希望这有助于阐明一些问题。
更新
我们现已发布 🔥🔥 Alamofire 4.0 🔥🔥 添加了 RequestAdapter
和 RequestRetrier
协议(protocol),让您可以轻松构建自己的身份验证系统,而无需考虑授权实现细节!更多信息,请引用我们的README其中有一个完整示例,说明如何在 OAuth2 系统上将其实现到您的应用中。
Full Disclosure: The example in the README is only meant to be used as an example. Please please please do NOT just go and copy-paste the code into a production application.
关于ios - Alamofire : How to handle errors globally,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28733256/