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

相關文章
相關標籤/搜索