Alamofire源码学习(六): RequestInterceptor请求拦截器

往期导航:

Alamofire源码学习目录合集swift

简介:

RequestInterceptor请求拦截器是一个协议,用来在请求流程中拦截请求,并对请求进行一些必要的处理,这是一个组合协议:RequestAdapter请求适配器与RequestRetrier请求重试器。使用者能够本身实现请求拦截器,根据本身的需求适配URLRequest,或者定义本身的重试逻辑,也可使用Alamofire默认实现的简易适配器,设置组合协议我的以为是由于,RequestAdapter是对请求的拦截预处理,RequestRetrier是对响应重试的拦截预处理,恰好请求响应一对,组合起来做为统一的一个对象来在生成Request时传入使用。api

RequestAdapter请求适配器:

只有一个方法,用来在Request建立好初始的URLRequest后,对URLRequest进行适配,适配处理先后均会告诉监听器对应的通知。数组

/// 入参为初始URLRequest, Session以及适配完成后的回调, 回调参数为Result对象, 能够为成功适配后的URLRequest对象, 也能够返回错误, 会向上抛出从建立requestAdaptationFailed错误
func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void)
复制代码

RequestRetrier请求重试器

也是只有一个方法,用来在请求失败时,决定是直接抛出错误,仍是依据必定的逻辑进行重试:markdown

// 参数为: Request对象, Session, 请求失败的错误信息以及重试逻辑回调, 回调参数为重试逻辑, 调用者根据该逻辑决定重试行为
    func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void)
复制代码

RetryResult重试逻辑

定义了四个重试逻辑session

public enum RetryResult {
    /// 马上重试
    case retry
    /// 延迟重试
    case retryWithDelay(TimeInterval)
    /// 不重试,直接完成请求
    case doNotRetry
    /// 不重试并抛出错误
    case doNotRetryWithError(Error)
}
//扩展一下快速取得相关信息, 两个可选值属性方便快速作出判断
extension RetryResult {
    /// 是否须要重试
    var retryRequired: Bool {
        switch self {
        case .retry, .retryWithDelay: return true
        default: return false
        }
    }
    
    /// 延迟重试时间
    var delay: TimeInterval? {
        switch self {
        case let .retryWithDelay(delay): return delay
        default: return nil
        }
    }
    
    /// 不重试并抛出错误时的错误信息
    var error: Error? {
        guard case let .doNotRetryWithError(error) = self else { return nil }
        return error
    }
}
复制代码

组合生成RequestInterceptor

直接把两个协议组合在一块儿供外部实现闭包

public protocol RequestInterceptor: RequestAdapter, RequestRetrier {}
//扩展一下,使得即使遵循协议也能够不实现方法,依旧不会报错
extension RequestInterceptor {
    public func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
        //直接返回原请求
        completion(.success(urlRequest))
    }

    public func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
        //不重试
        completion(.doNotRetry)
    }
}
复制代码

Alamofire预先定义的请求拦截器:

Alamofire预先定义了一些简易的拦截器,能够直接使用,也能够根据本身需求自行实现post

1. Adapter:基于闭包的简易请求适配器:

// 先定义了一个用来适配请求的闭包:
public typealias AdaptHandler = (URLRequest, Session, _ completion: @escaping (Result<URLRequest, Error>) -> Void) -> Void
//而后实现基于闭包的请求适配器,只是简单的持有一个闭包来适配请求:
open class Adapter: RequestInterceptor {
    private let adaptHandler: AdaptHandler

    public init(_ adaptHandler: @escaping AdaptHandler) {
        self.adaptHandler = adaptHandler
    }

    open func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
        adaptHandler(urlRequest, session, completion)
    }
}
复制代码

2. Retrier:基于闭包的简易重试器:

//先定义了一个用来决定重试逻辑的闭包:
public typealias RetryHandler = (Request, Session, Error, _ completion: @escaping (RetryResult) -> Void) -> Void
//而后实现基于闭包的重试器:
open class Retrier: RequestInterceptor {
    private let retryHandler: RetryHandler

    public init(_ retryHandler: @escaping RetryHandler) {
        self.retryHandler = retryHandler
    }

    open func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
        retryHandler(request, session, error, completion)
    }
}
复制代码

3. Interceptor:能够持有多个适配器与重试器

持有两个数组,一个保存适配器对象,一个保存重试器对象,而后重试的时候,挨个去处理,所以要注意传入时的顺序问题学习

open class Interceptor: RequestInterceptor {
    /// 保存适配器, 有任何一个出现错误, 就会抛出错误
    public let adapters: [RequestAdapter]
    /// All `RequestRetrier`s associated with the instance. These retriers will be run one at a time until one triggers retry.
    /// 保存重试器, 有任何一个出现了须要重试(当即重试或者延迟重试)就会中止, 而后抛出须要重试. 有任何一个不重试并抛出错误也会中止, 并抛出错误.
    public let retriers: [RequestRetrier]
    /// 也可使用重试器与适配器回调来建立单个的组合器
    public init(adaptHandler: @escaping AdaptHandler, retryHandler: @escaping RetryHandler) {
        adapters = [Adapter(adaptHandler)]
        retriers = [Retrier(retryHandler)]
    }
    /// 用两个数组初始化
    public init(adapter: RequestAdapter, retrier: RequestRetrier) {
        adapters = [adapter]
        retriers = [retrier]
    }
    /// 用适配器+重试器+拦截器数组初始化, 会把拦截器数组均加入到适配器与重试器数组中
    public init(adapters: [RequestAdapter] = [], retriers: [RequestRetrier] = [], interceptors: [RequestInterceptor] = []) {
        self.adapters = adapters + interceptors
        self.retriers = retriers + interceptors
    }
    /// 适配器代理方法, 调下面的私有方法
    open func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
        adapt(urlRequest, for: session, using: adapters, completion: completion)
    }
    /// 私有适配方法, 会不停递归
    private func adapt(_ urlRequest: URLRequest, for session: Session, using adapters: [RequestAdapter], completion: @escaping (Result<URLRequest, Error>) -> Void) {
        // 用来准备递归的数组
        var pendingAdapters = adapters
        // 递归空了就执行回调并返回
        guard !pendingAdapters.isEmpty else { completion(.success(urlRequest)); return }
        // 取出第一个适配器
        let adapter = pendingAdapters.removeFirst()
        // 走你
        adapter.adapt(urlRequest, for: session) { result in
            switch result {
            case let .success(urlRequest):
                // 适配经过, 递归去适配剩下的
                self.adapt(urlRequest, for: session, using: pendingAdapters, completion: completion)
            case .failure:
                // 适配失败, 直接抛出错误
                completion(result)
            }
        }
    }
    // 重试器逻辑, 调下面私有方法
    open func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
        retry(request, for: session, dueTo: error, using: retriers, completion: completion)
    }
    // 私有重试逻辑, 会递归调用
    private func retry(_ request: Request, for session: Session, dueTo error: Error, using retriers: [RequestRetrier], completion: @escaping (RetryResult) -> Void) {
        // 用来递归的重试器数组
        var pendingRetriers = retriers
        // 递归完成且没有触发重试或错误, 就返回不重试, 并返回不重试
        guard !pendingRetriers.isEmpty else { completion(.doNotRetry); return }
        // 取出第一个
        let retrier = pendingRetriers.removeFirst()
        // 走你
        retrier.retry(request, for: session, dueTo: error) { result in
            switch result {
            case .retry, .retryWithDelay, .doNotRetryWithError:
                // 重试, 延迟重试, 不重试并抛出错误的话, 执行回调
                completion(result)
            case .doNotRetry:
                // 不然开始递归
                self.retry(request, for: session, dueTo: error, using: pendingRetriers, completion: completion)
            }
        }
    }
}
复制代码

以上纯属我的理解,不免有错误,若是发现有错,欢迎评论指出~~将第一时间改正,也欢迎评论讨论,很是感谢~~ui

相关文章
相关标签/搜索