一尘不染

Alamofire:如何全局处理错误

swift

我的问题与这一问题非常相似,但对于Alamofire:AFNetworking:全局处理错误并重复请求

如何能够全局捕获错误(通常为401)并在发出其他请求之前进行处理(如果不进行管理则最终失败)?

我当时想链接一个自定义响应处理程序,但这对应用程序的每个请求都很愚蠢。
也许是子类化,但是我应该子类化处理该类?


阅读 404

收藏
2020-07-07

共1个答案

一尘不染

鉴于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令牌
  • 解析响应
  • 将解析的响应强制转换为适当的类型(泛型)

希望这有助于阐明。


更新资料

现在,我们发布了🔥🔥Alamofire
4.0🔥🔥,其中添加了RequestAdapterRequestRetrier协议,无论授权实现的详细信息如何,您都可以轻松构建自己的身份验证系统!有关更多信息,请参阅我们的自述文件,其中提供了有关如何在OAuth2系统上实现到您的应用程序的完整示例。

全面披露: 自述文件中的示例仅用作示例。请请不要只是将代码复制粘贴到生产应用程序中。

2020-07-07