Alamofire之Response

在前面的几篇文章中已经介绍了Alamofire的request。本篇咱们一块儿来了解一下response。swift

1、reponse函数

说到response不少小伙伴都会和序列化联系起来,那么Alamofire的response的做用究竟是什么呢?数组

咱们来看个栗子🌰:网络

let urlBD = "https://www.baidu.com"

Alamofire.request(urlBD, method: .get, parameters: ["user": "bo"])
    .response { (response) in
        print(response)
}
复制代码

运行结果会打印不少内容,因篇幅缘由就不贴图了,就留给小伙伴自行玩耍吧。session

这些 response 中的内容是如何来的呢?咱们一块儿来看看源码。 闭包

从源码能够看出,response 函数并无作序列化的处理。而是使用 Requestself.requestself.responseself.delegateself.timeline 属性封装成一个新的对象 DefaultDataResponse返回给 completionHandler 闭包。app

由于 response 函数是 DataRequest 的扩展。因此 self.requestself.response 比较容易理解。那么 self.delegate.data 是什么呢?咱们点击查看源码: dom

能够看到,若是 dataStream 闭包为空,则返回 mutableData 。因此咱们再继续查看 mutableData 是何时赋值的。 异步

咱们找到在 func urlSession(session:dataTask:didReceive:) 回调中,返回的数据 data 会被添加到 mutableData 中。函数

为何须要 mutableData 呢?由于请求的数据多是分段返回的,因此须要一个中间变量来存储每次返回的数据。工具

self.delegate.data 相似, self.delegate.error 也是在回调函数 didCompleteWithError 中判断,若是有错误,则给 error 赋值。

至于 self.timeline,咱们在上一篇 Alamofire之request补充 已有介绍,这里再也不赘述,想了解的小伙伴请移步。

综上,能够总结出,其实 response 函数并无作其余处理,仅作为保存数据和返回数据的载体,其整合了request请求的各个返回结果,做为一个总体返回给用户,供用户随时取用。

2、序列化

在查看 response 函数源码的时候,若是往下翻小伙伴可能会发现另外一个 reponse 函数。

这个 response 函数和上一个相比,多了一个 responseSerializer 参数,这个参数就是序列化器。

那么这个 response 函数该如何使用呢?咱们先来分析 responseSerializer 参数是个什么类型。

responseSerializer 的参数是一个泛型。泛型须要实现一个协议: DataResponseSerializerProtocol

同时咱们在其下方发现一个已经实现该协议的类型 DataResponseSerializer

因此咱们可使用这个类做为序列化器。

let urlBD = "https://www.baidu.com"

Alamofire.request(urlBD, method: .get, parameters: ["user": "bo"])
    .response { (response) in
        print(response)
    }
    .response(responseSerializer: DataResponseSerializer<String>.init(serializeResponse: { (request, response, data, error) -> Result<String> in
        
        if error == nil {
            
            return .success("网络请求成功")
        } else {
            return .failure(NSError(domain: "网络请求失败", code: 10080, userInfo: nil))
        }
    })) { (reponse) in
        
        print("序列化结果: \(reponse)")
}
复制代码

DataResponseSerializer 初始化须要传入一个闭包,且闭包的返回值为 Result 类型。

从运行结果会发现,在打印出未序列化的 reposne 以后,会再打印出序列化后的结果。

以上就是response序列化器的分析,包括 Alamofire提供的 responseJSON 函数,其内部仅是对 JSON序列化器作了一次封装而已。

3、多表单上传

先举个栗子🌰:

let url = "http://www.baidu.com/"

Alamofire.upload(multipartFormData: { (formData) in
    
    formData.append("BO".data(using: .utf8)!, withName: "name")
    formData.append("122345".data(using: .utf8)!, withName: "pwd")
    
}, to: url) { (result) in
    
    print(result)
}
复制代码

上面👆这段代码就是多表单上传的例子。而后使用抓包工具 Charles 抓包。

就能够看到如上所示的结果。

其实在进行多表单上传时,须要将表单数据按照必定的格式(如上图)封装到httpBody中。具体格式请小伙伴自行google。

那么Alamofire是如何封装的呢?咱们一路查看源码,很容易就能找到这里:

首先由于在读取数据时多是一个耗时操做,因此

  • 在异步线程中初始化一个 MultipartFormData 对象。
  • 再调用 multipartFormData 闭包,拼接表单数据。
  • 在发起网络请求以前,须要先设置 Content-Type,代表这是一个表单数据,以及 boundary 边界信息。
  • 再调用 formData.encode() 格式化数据。

一、添加表单

咱们在向表单中添加数据时,会调用 append 函数添加数据。

append 函数中,参数 name 会被先传入 contentHeaders 函数中进行处理。

通过处理后会成为咱们所须要的格式。

而后再将格式化后的 headersdata 封装成 BodyPart 对象。

最后添加到 bodyParts 数组中保存起来。

二、encode表单

须要格式化表单时,会调用 encode 函数。

encode 函数会将 bodyParts 中的首尾两个元素分别设置 hasInitialBoundary = truehasFinalBoundary = true,标识出其是首尾元素。

再遍历 bodyParts 数组中的元素,对每个元素分别进行 encode

一、格式化边界

首先经过 hasInitialBoundary 判断是首元素仍是中间元素。

  • 若是是首元素,则调用 initialBoundaryData() 函数。
  • 若是是中间元素,则调用 encapsulatedBoundaryData() 函数。
  • 若是是尾元素,则调用 finalBoundaryData() 函数。

这三个函数调用的都是 boundaryData 函数,仅参数不一样。

因此,最后生成的边界格式会有所不一样。如: 首元素:--alamofire.boundary.xxx 中间元素:--alamofire.boundary.xxx 尾元素:--alamofire.boundary.xxx--

二、格式化name

对每一个元素调用 encodeHeaders 函数。

最后生成格式以下: Content-Disposition: form-data; name="name"

三、格式化data

对每一个元素调用 encodeBodyStream 函数。

由于表单数据多是一个很大的文件,若是要将文件整个读取到内存中,将会消耗掉很大的内存空间,形成资源紧张。因此使用 数据流stream 的方式读取数据。生成格式如:BO。

最后将生成的全部数据拼接为一个data数据,完成表单数据的封装。

以上,则是多表单上传中,表单数据的格式化解析。

相关文章
相关标签/搜索