Swift编程swift 文章收集swift

Alamofire之Response

2019-08-25  本文已影响0人  慕_風

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

一、reponse函数

说到response很多小伙伴都会和序列化联系起来,那么Alamofire的response的作用到底是什么呢?

我们来看个栗子🌰:

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

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

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

这些 response 中的内容是如何来的呢?我们一起来看看源码。

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

因为 response 函数是 DataRequest 的扩展。所以 self.requestself.response 比较容易理解。那么 self.delegate.data 是什么呢?我们点击查看源码:

可以看到,如果 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

15667180724390.jpg

所以我们可以使用这个类作为序列化器。

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是如何封装的呢?我们一路查看源码,很容易就能找到这里:

首先因为在读取数据时可能是一个耗时操作,所以

1、添加表单

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

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

经过处理后会成为我们所需要的格式。

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

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

2、encode表单

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

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

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

1、格式化边界

首先通过 hasInitialBoundary 判断是首元素还是中间元素。

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

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

2、格式化name

对每个元素调用 encodeHeaders 函数。

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

3、格式化data

对每个元素调用 encodeBodyStream 函数。

因为表单数据可能是一个很大的文件,如果要将文件整个读取到内存中,将会消耗掉很大的内存空间,造成资源紧张。所以使用 数据流stream 的方式读取数据。生成格式如:BO。

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

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

上一篇 下一篇

猜你喜欢

热点阅读