在前面的几篇文章中已经介绍了Alamofire的request。本篇咱们一块儿来了解一下response。swift
说到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
函数并无作序列化的处理。而是使用 Request
的 self.request
、self.response
、self.delegate
、self.timeline
属性封装成一个新的对象 DefaultDataResponse
返回给 completionHandler
闭包。app
由于 response
函数是 DataRequest
的扩展。因此 self.request
、self.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请求的各个返回结果,做为一个总体返回给用户,供用户随时取用。
在查看 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序列化器作了一次封装而已。
先举个栗子🌰:
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
函数中进行处理。
通过处理后会成为咱们所须要的格式。
而后再将格式化后的 headers
和 data
封装成 BodyPart
对象。
最后添加到 bodyParts
数组中保存起来。
须要格式化表单时,会调用 encode
函数。
encode
函数会将 bodyParts
中的首尾两个元素分别设置 hasInitialBoundary = true
和 hasFinalBoundary = 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数据,完成表单数据的封装。
以上,则是多表单上传中,表单数据的格式化解析。