美文网首页
Alamofire之Request基本流程

Alamofire之Request基本流程

作者: 越来越胖了 | 来源:发表于2019-08-23 17:54 被阅读0次

    首先来看以下两段代码:

    Alamofire.request("https://httpbin.org/get").responseJSON { response in
                print(" request ==   \(response.request as Any)")  // 原始的URL请求    https://httpbin.org/get
                print("response =    \(response.response as Any)") // HTTP URL响应   一些请求头信息
                print("data =    \(response.data as Any)")     // 服务器返回的数据      376 bytes   376字节
                print("result =    \(response.result as Any)")   // 响应序列化结果,在这个闭包里,存储的是JSON数据
                
                if let JSON = response.result.value {
                    print("JSON: \(JSON)")
                }
            }
    
    SessionManager.default.request("https://httpbin.org/get").responseJSON { response in
                print(" request ==   \(response.request as Any)")  // 原始的URL请求    https://httpbin.org/get
                print("response =    \(response.response as Any)") // HTTP URL响应   一些请求头信息
                print("data =    \(response.data as Any)")     // 服务器返回的数据      376 bytes   376字节
                print("result =    \(response.result as Any)")   // 响应序列化结果,在这个闭包里,存储的是JSON数据
                
                if let JSON = response.result.value {
                    print("JSON: \(JSON)")
                }
            }
    
    

    其实上面的两种写法是一样的,现在就第一种来分析下Alamofire进行网络请求的基本流程:

    1.SessionManager.default.request

    public func request(
        _ url: URLConvertible,
        method: HTTPMethod = .get,
        parameters: Parameters? = nil,
        encoding: ParameterEncoding = URLEncoding.default,
        headers: HTTPHeaders? = nil)
        -> DataRequest
    {
        return SessionManager.default.request(
            url,
            method: method,
            parameters: parameters,
            encoding: encoding,
            headers: headers
        )
    }
    

    进行了很多的默认值的设置,这也是为什么我们可以传一个url直接请求的原因,SessionManager.default如下:

     public static let `default`: SessionManager = {
            let configuration = URLSessionConfiguration.default
            configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
    
            return SessionManager(configuration: configuration)
        }()
    

    调用了SessionManager(configuration: configuration) 初始化方法; 初始化具体干了什么鬼操作呢?

    public init(
            configuration: URLSessionConfiguration = URLSessionConfiguration.default,
            delegate: SessionDelegate = SessionDelegate(),
            serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
        {
            self.delegate = delegate
            self.session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
    
            commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
        }
    
      1. SessionManager类中创建了一个 SessionDelegate类,用于处理所有的代理事件,完成了代理的移交;
    public let delegate: SessionDelegate
    
      1. 通过self.session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)创建URLSession

    下面是commonInit的方法实现:

     private func commonInit(serverTrustPolicyManager: ServerTrustPolicyManager?) {
            session.serverTrustPolicyManager = serverTrustPolicyManager
    
            delegate.sessionManager = self
    
            delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
                guard let strongSelf = self else { return }
                DispatchQueue.main.async { strongSelf.backgroundCompletionHandler?() }
            }
        }
    

    delegate.sessionManager = self , SessionManager类中创建了一个SessionDelegate类.SessionDelegate类里面传入了SessionManager,形成一个回环,然后通过weak打破循环引用.

    delegate.sessionDidFinishEventsForBackgroundURLSession这个是后台下载完成的回调,这个前面章节介绍过了.

    初始化过程总结一下就是:

    • URLSession的配置
    • 代理的移交,
    • 创建了一个后台下载完成的回调闭包

    2. request

    open func request(
            _ url: URLConvertible,
            method: HTTPMethod = .get,
            parameters: Parameters? = nil,
            encoding: ParameterEncoding = URLEncoding.default,
            headers: HTTPHeaders? = nil)
            -> DataRequest
        {
            var originalRequest: URLRequest?
    
            do {
                originalRequest = try URLRequest(url: url, method: method, headers: headers)
                let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters)
                return request(encodedURLRequest)
            } catch {
                return request(originalRequest, failedWith: error)
            }
        }
    

    我们就传了一个URL URLConvertible = https://httpbin.org/get 其他的使用默认值(swift的可以这样干😑);
    创建URLRequest并进行相应的信息配置:

    originalRequest = try URLRequest(url: url, method: method, headers: headers)
    
    

    通过url,method,headers创建URLRequest(通过url创建request,用request创建task);

    let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters)参数处理,Parameters是要传递什么参数,比如登录的username,password:

     public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
            var urlRequest = try urlRequest.asURLRequest()
    
            guard let parameters = parameters else { return urlRequest }
    
            if let method = HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET"), encodesParametersInURL(with: method) {
                guard let url = urlRequest.url else {
                    throw AFError.parameterEncodingFailed(reason: .missingURL)
                }
    
                if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty {
                    let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters)
                    urlComponents.percentEncodedQuery = percentEncodedQuery
                    urlRequest.url = urlComponents.url
                }
            } else {
                if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
                    urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
                }
    
                urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false)
            }
    
            return urlRequest
        }
    

    通过ifelse知道主要把请求分成两种情况处理,进入到if中的可以 是.get, .head, .delete,其他为else方法实现:

     private func encodesParametersInURL(with method: HTTPMethod) -> Bool {
           …
            switch method {
            case .get, .head, .delete:
                return true
            default:
                return false
            }
        }
    

    得到完整的请求percentEncodedQuery赋值给urlComponents.percentEncodedQuery,URLComponents 是一个结构体,可以保存信息;

    下面对着get,post两种情况进行分析:

    1.get

    let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters)
    
    

    分析:

    • (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") 为路由的处理;
    • query(parameters) 为传入的参数的处理;对参数进行百分号编码处理,其中query(parameters)主要是对传递的参数进行递归处理,拼接:
    
    private func query(_ parameters: [String: Any]) -> String {
            var components: [(String, String)] = []
    //以下为分情况递归遍历,拿到传入的所有的数据🍎
            for key in parameters.keys.sorted(by: <) {
                let value = parameters[key]!
    //key 和value进行组装,返回的数组里面的放的是元祖🍎
                components += queryComponents(fromKey: key, value: value)
            }
           //例: username=XXXX&passward=OOOO     XXXX,OOOO 为百分号编码结果🍎
            return components.map { "\($0)=\($1)" }.joined(separator: "&")
        }
    //递归处理🍎
        public func queryComponents(fromKey key: String, value: Any) -> [(String, String)] {
            var components: [(String, String)] = []
    
            if let dictionary = value as? [String: Any] {
                for (nestedKey, value) in dictionary {
                    components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value)
                }
            } else if let array = value as? [Any] {
                for value in array {
                    components += queryComponents(fromKey: arrayEncoding.encode(key: key), value: value)
                }
            } else if let value = value as? NSNumber {
                if value.isBool {
                    components.append((escape(key), escape(boolEncoding.encode(value: value.boolValue))))
                } else {
                    components.append((escape(key), escape("\(value)")))
                }
            } else if let bool = value as? Bool {
                components.append((escape(key), escape(boolEncoding.encode(value: bool))))
            } else {
                components.append((escape(key), escape("\(value)")))
            }
    
            return components
        }
    
    

    2.post

    if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
                    urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
                }
    
                urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false)
    
    

    可以看到,post是把学习存储在了httpBody,同时数据是进行了处理的;一般我们传递给服务器时,后台也会要求我们把数据转成json发送,比如下面这样:

    extension ViewController {
        // options: .prettyPrinted  使用空白和缩进使输出更具可读性的写入选项
        fileprivate func getJsonFromArray(_ array:Array<Any>) -> String{
            let data = try? JSONSerialization.data(withJSONObject: array, options: .prettyPrinted)
            return String(data: data!, encoding: .utf8) ?? ""
        }
    }
    

    post其他具体的就不再深究了.

    数据处理完成后调用request(encodedURLRequest),传入处理好的数据:

    
    open func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
            var originalRequest: URLRequest?
    
            do {
                //取出之前配置的URLRequest;🍎
                originalRequest = try urlRequest.asURLRequest()
                //设置类的结构体中的成员urlRequest的值,其实就是保存了URLRequest🍎
                let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)
                ////创建task任务🍎
                let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
                let request = DataRequest(session: session, requestTask: .data(originalTask, task))
    
                delegate[task] = request
    
                if startRequestsImmediately { request.resume() }
    
                return request
            } catch {
                return request(originalRequest, failedWith: error)
            }
        }
    
    

    说明:

    • let originalTask = DataRequest.Requestable(urlRequest: originalRequest!);设置类的结构体中的成员urlRequest的值,其实就是默认初始化一个结构体,并设置结构体中的属性urlRequest的值,也就是保存了URLRequest;所以可以理解为这里是一个储存型结构体.结构体的一种常规使用方法,值得我们学习;
    • 为什么要这样干?其实也可以直接DataRequest类中定义一个task属性,通过DataRequest去创建,但是这样的话,task相关的所有操作都需要DataRequest去处理,而且DataRequesttask不应该有一个包含关系,他们更像是同一级的,由DataRequest去创建task,会导致DataRequest过重;
    • 而既然这样那为什么不定义一个task类去处理呢 ? 因为我们需要保存urlRequest的值;
    • 最后,我们的DataRequesttask是有对应关系的,通过结构体把这个依赖打破了,降低耦合,那taskDataRequest的对应关系怎么办? 关联方法---> delegate[task] = request;

    let task = try originalTask.task(session: session, adapter: adapter, queue: queue),调用结构体中的task方法,创建task任务,实现方法中调用了{ session.dataTask(with: urlRequest) } 同步创建task,这里传入了一个adapt,这个参数下一章节再介绍.

    //这个是DataRequest中的结构体Requestable🍎
     struct Requestable: TaskConvertible {
            let urlRequest: URLRequest
    
            func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
                do {
                    let urlRequest = try self.urlRequest.adapt(using: adapter)
                    return queue.sync { session.dataTask(with: urlRequest) }
                } catch {
                    throw AdaptError(error: error)
                }
            }
        }
    

    以上主要就是创建task任务,同时在结构体中保存了URLRequest

    Request.swift中的类其实有4种,都是,Request的子类,——根据不同需求会调用不同的类的方法;如下载时,我们调用的不再是DataRequest类,而是DownloadRequest类,同时所有的子类继承了Request的方法,因为这些方法是每个子类所必须要有的功能,所以写在一起了这里.这种写法也是值得大家去参考的,主要是共有的功能的抽离形成父类.

    let request = DataRequest(session: session, requestTask: .data(originalTask, task))其实就是调用父类的初始化方法,这一句才是DataRequest的初始化,所以很明显能感觉到,前面的task的创建和DataRequest是分离的.而创建的类型通过枚举requestTask传参决定,这就是工厂设计模式,同时这里也涉及到枚举的常规所用,这些都是非常值得我们学习的地方.

    open class Request {
    …
     enum RequestTask {
            case data(TaskConvertible?, URLSessionTask?)
            case download(TaskConvertible?, URLSessionTask?)
            case upload(TaskConvertible?, URLSessionTask?)
            case stream(TaskConvertible?, URLSessionTask?)
        }
    …
    init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
            self.session = session
    //data传参决定创建类型,典型的工厂设计🍎
            switch requestTask {
            case .data(let originalTask, let task):
                taskDelegate = DataTaskDelegate(task: task)
                self.originalTask = originalTask
            case .download(let originalTask, let task):
                taskDelegate = DownloadTaskDelegate(task: task)
                self.originalTask = originalTask
            case .upload(let originalTask, let task):
                taskDelegate = UploadTaskDelegate(task: task)
                self.originalTask = originalTask
            case .stream(let originalTask, let task):
                taskDelegate = TaskDelegate(task: task)
                self.originalTask = originalTask
            }
    
            delegate.error = error
            delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
        }
    …
    }
    
    
    • DataRequest的初始化 还创建了一个taskDelegate,让self.originalTask = 创建的taskdelegate,把两个delegate关联就在这个方法里面;
    • let originalTask: TaskConvertible? , self是Request类,self.originalTask其实就是Request中的TaskConvertible对象;

    我们前面提到了一个代理的移交,回顾一下: URLSession的代理———>移交给了SessionManager类中的 SessionDelegate类;当请求的数据返回时,通过系统的URLSession的代理拿到数据;因为代理移交的关系,会响应SessionDelegate类里面的方法:

    open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
            /// Executed after it is determined that the request is not going to be retried
    
    //一个闭包🍎
            let completeTask: (URLSession, URLSessionTask, Error?) -> Void = { [weak self] session, task, error in
                guard let strongSelf = self else { return }
    //self是SessionDelegate类, 这里的意思是,对外的方法taskDidComplete用户是否实现?如果实现了,把数据传给外界.🍎
    //实现方法就是这样:SessionManager.default.delegate.taskDidComplete= {…}🍎
                strongSelf.taskDidComplete?(session, task, error)
    //strongSelf[task]下标法去到Request,然后任务下发,而delegate就是taskDelegate(内部定义的属性),上面我们说到了,self.originalTask = 创建的taskdelegate,🍎
    //也就意味着,  .urlSession方法会调用TaskDelegate的urlSession方法🍎
                strongSelf[task]?.delegate.urlSession(session, task: task, didCompleteWithError: error)
    //这下面是通知的响应,流程和上面是一样的,先不管🍎
                var userInfo: [String: Any] = [Notification.Key.Task: task]
    
                if let data = (strongSelf[task]?.delegate as? DataTaskDelegate)?.data {
                    userInfo[Notification.Key.ResponseData] = data
                }
    
                NotificationCenter.default.post(
                    name: Notification.Name.Task.DidComplete,
                    object: strongSelf,
                    userInfo: userInfo
                )
    
                strongSelf[task] = nil
            }
    
    //下面是一系列判断后对上面那个闭包的调用,所以下面的不看了,我们分析上面的闭包
    
       ...一大堆判断后调用
                completeTask(session, task, error)
      ...
    
    

    下发到具体的TaskDelegate的urlSession方法中:

    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
            if let taskDidCompleteWithError = taskDidCompleteWithError {
    //是否自己实现了错误处理,是的话,传出去🍎
                taskDidCompleteWithError(session, task, error)
            } else {
                if let error = error {
    //是否有错误,有的话进行错误处理🍎
                    if self.error == nil { self.error = error }
    
                    if
                        let downloadDelegate = self as? DownloadTaskDelegate,
                        let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data
                    {
    
                        downloadDelegate.resumeData = resumeData
                    }
                }
    //任务队列开启🍎
                queue.isSuspended = false
            }
        }
    

    总结一下就是:

    • 创建URLRequest
    • 创建task任务,同时在结构体中保存了URLRequest
    • 通过URLSessiontask创建一个Request
    • Request进行resume
    • 到此.request解析完成,返回了request,形成链式请求.

    未完待续,下一章节继续分析.

    相关文章

      网友评论

          本文标题:Alamofire之Request基本流程

          本文链接:https://www.haomeiwen.com/subject/fjetectx.html