我的问题和这个很相似,但对于阿拉莫菲尔来说:AFNetworking: Handle error globally and repeat request

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

我想链接一个自定义响应处理程序,但在应用程序的每个请求上都这样做是愚蠢的

推荐答案

鉴于NSURLSessions的并行性质,在oauth流中处理401响应的刷新相当复杂.我花了相当长的时间构建了一个内部解决方案,它对我们非常有效.下面是对如何实现它的一般 idea 的一个非常高级的提取.

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

希望这能帮助我们了解一些情况.


使现代化

我们现在已经发布了?? 阿拉莫菲尔4.0?? 它添加了RequestAdapterRequestRetrier协议,允许您轻松构建自己的身份验证系统,而不考虑授权实现细节!有关更多信息,请参阅我们的README,其中有一个完整的示例,说明如何在OAuth2系统上实现应用程序.

Full Disclosure:自述文件中的示例仅用作示例.请不要只是go 复制粘贴到生产应用程序的代码.

Swift相关问答推荐

SwiftUI状态更新

MyApp类型不符合协议App''''

不使用`lockFocus`将两个重叠的NSImage转换为单个NSImage

通过SwiftUI中的列表 Select 从字典中检索值

从AppKit打开SwiftUI设置

可选,类似于自定义类型的初始化

关闭 SwiftUI TabView 中的子视图

使用索引来访问 libc 定义的元组

在Swift中如何将NSUrl转换为本地路径URL

Xcode创建GitHub仓库,但未推送所有文件

如何从 Swift 中隐藏 Objective-C 类接口的一部分?

覆盖一个子元素的 HStack 对齐方式

我如何读取并通知可可 macos 中某个类的计算(computed)属性?

需要将 json 映射到 Swift 中的模型

如何使用带有 span 样式标签的 Swift XMLParser

这是 Int64 Swift Decoding 多余的吗?

为什么 Apple Scrumdinger Sample App 使用两个事实来源?

Type 应该采用什么协议来让泛型函数将任何数字类型作为 Swift 中的参数?

在 xcode8 中找不到 ModuleName-Swift.h 文件

在 Swift 中从服务器播放视频文件