前言
在前面几篇内容中已经大致介绍了Alamofire
的Request
请求,当一个Request
完成的时候,下一步 肯定要处理服务器返回的响应数据。本篇内容就记录一下学习处理响应数据Response
的内容。
Response
先来一个简单的代码例子看一下下:
SessionManager.default.request("https://www.douban.com/j/app/radio/channels")
.response { (response) in
debugPrint(response)
}
我们都知道Alamofire
可以使用链式访问,那就搞点事做,把request
和response
换一下,先进行response
,再进行request
,换了之后会发现根本不可能。这就说明了request
必须要在response
之前访问,这也提现了request
的重要性
好了,在皮一下之后,回到代码来看,看一下这个response
是怎么实现的:
这里可以看到
response
的任务是加入到了 delegate.queue.addOperation
,这个delegate.queue
就是在发起request
之后,创建的TaskDelegate
会默认初始化一个挂起队列。(具体请看上篇时间轴部分)然后在request
请求Complete
之后再取消队列的挂起,保证了队列中的任务全部是在request
请求完成之后进行的,这同样也更加说明了request
必须要在response
之前访问。
然后任务被交给了主队列(毕竟开发者有可能利用response
进行UI
的更新),可以看到在这里并没有对response
进行序列化,而是初始化了一个DefaultDataResponse
类的对象,
DefaultDataResponse
是用来保存self.request
、self.response
、self.delegate.data
、self.delegate.error
、self.timeline
、self.delegate.metrics
属性,并把初始化的DefaultDataResponse
对象返回给completionHandler
闭包。(有点类似于Model
,一般来说,在swift
中,如果只是为了保存数据,那么应该把这个类设计成struct
。struct
是值传递,因此对数据的操作更安全。除了定义需要保存的数据属性后,必须设计一个符合要求的构造函数。),
小总结:
request
(该响应来源于那个请求)、response
(服务器返回的响应)、data
(响应数据)、error
(在请求中可能发生的错误)、timeline
(请求的时间轴封装)、_metrics
(包含了请求和响应的统计信息) 这些数据都不是由response
来创建的,它只有使用权。也就是说response
只是一个数据的保存信息者,把各种数据化零为整返回给用户。
Response序列化
在Alamofire
中把Request
分为了4类:
DataRequest
DownloadRequest
UploadRequest
StreamRequest
因为有四种不同的Request
类型,StreamRequest
我们先a按下不表,对于UploadRequest
来说,服务器响应的数据比较简单,就响应一个结果就行,因此不需要对它的Response
专门进行封装。因此,Alamofire
设计了2种与之相对应的Response
类型
-
DefaultDataResponse
/DataResponse
-
DefaultDownloadResponse
/DataResponse
细心的你一定会发现在DataRequest
的扩展的中会有两个response
方法,(第一个response
方法在上面已经给出,下面贴出第二个response
)
在这个序列化的
response
方法内部,可以看到会先进行一个serializeResponse
的操作得到一个结果result
,然后在初始化一个DataResponse
对象用来保存 self.request
、self.response
、self.delegate.data
、result
、self.timeline
,self.delegate.metrics
这些数据,并把DataResponse
对象返回给completionHandler
闭包。
如果你对比这两个response
方法,聪明而优秀的你就会发现这两个方法的不同之处:
在request
请求完成之后,获取到的是没有经过序列化后的数据,如果调用了没有序列化的response
方法,DefaultDataResponse/DefaultDownloadResponse
。如果调用了序列化的response
方法,需要传入一个泛型的responseSerializer
参数,返回的就是DataResponse
。
可以看到这个序列化的
response
方法遵循了DataResponseSerializerProtocol
协议,下面通过源码看一下这个协议的具体实现:
在这个协议内部有一个序列化函数
serializeResponse
,并返回一个类型为SerializedObject的Result
,那么也就是说序列者只需要遵循这个协议就可以了:所以就有了这样一个用序列化函数serializeResponse
作为参数来初始化,并且保存序列化函数的DataResponseSerializer
类;
从这里我们知道调用序列化函数serializeResponse
会返回一个Result
,而且在前面也已经说过,在序列化的response
方法内部,看到会先进行一个serializeResponse
的操作得到一个结果result
,
可以看到关于
Result
的结果只有两种:成功和失败。成功就把序列化完成的数据返回,失败就返回一个error
信息。
Response扩展
如果我想把请求的结果序列化成json
类型或者String
类型的,怎么办,难道调用序列化的response
方法拿到返回的内容之后,用户此时再去自己做序列化成json
类型或者String
类型的内容?怎么可能,霸气侧漏的Alamofire
早就已经都做好了。
SessionManager.default.request("https://www.douban.com/j/app/radio/channels")
.response { (response) in
}
SessionManager.default.request("https://www.douban.com/j/app/radio/channels")
.responseData { (responsedata) in
}
SessionManager.default.request("https://www.douban.com/j/app/radio/channels")
.responseString { (responseString) in
}
SessionManager.default.request("https://www.douban.com/j/app/radio/channels")
.responseJSON { (responsejson) in
}
SessionManager.default.request("https://www.douban.com/j/app/radio/channels")
.responsePropertyList{ (responselist) in
}
responseData
responseData
在其方法内部是直接调用了response
的序列化方法,那么也就是和response
一样,把数据序列化为Data
类型也就是Result
。
这里传入的序列化器responseSerializer
的参数是DataRequest.dataResponseSerializer()
,下面来看一下dataResponseSerializer()
:
返回的就是
DataResponseSerializer
类型的序列化器,DataResponseSerializer
这个类前面已经介绍过,它是用来保存序列化函数的,关键在于Request.serializeResponseData
,
这里会根据序列化的成功和失败来返回数据,返回
.success(validData)
或者 .failure(error信息)
。
注解:emptyDataStatusCodes
如果
HTTP response code
是204
或者205
,就表示Data
为nil
.
关于responseString
、responseJSON
、responsePropertyList
在内部是如何实现的,在这里就不一一列出来了,基本和responseData
的套路一样,有兴趣的大佬可以借助源码去看一下。
总结
实际上这个response
并不是我们常见的那种response
,它只是一个保存内容的载体,它把request
、response
、data
、error
、timeline
、_metrics
这些数据全部收集整合起来,放到自己身上,然后一起返回给用户,这样用户在外面可以拿到他想要的任何数据,这是多么牛X的处理方式。