上篇文章AFNetworking3.1源码解读<一>主要学习了Session相关部分,本篇主要学习Serialization中的AFURLRequestSerialization部分。AFURLRequestSerialization顾名思义是请求序列化,也就是创建出符合HTTP协议的GET、POST、HEAD、PUT、DELETE等请求。在AFNetworking中RequstSerialization主要实现了普通请求和multipart/form-data请求的序列化。
普通请求序列化
HTTP Method 是GET、HEAD、DELETE
如果HTTP Method 是GET、HEAD、DELETE则请求会被序列化成Query String,比如在bing里面搜索helloworld后请求的URL为
其中q=helloworld&go=Submit&qs=n&form=QBLH&pq=helloworld&sc=8-10&sp=-1&sk=&cvid=C1DAD98D70EA4C0CB1B38F3E04016570就是查询字符串,参数间用&分割,参数如果是嵌套Dictionary怎会变成[key][key1]=[value1]&[key][key2]=[value2],如果是嵌套的是Array怎会变成key[]=value1&key[]=value2。在AFNetworking中Query string请求序列化一定得是NSDctionary类型。
其他
其他主要指的是POST请求,POST请求序列化时分为AFURLRequestSerialization、AFJSONRequestSerializer、AFPropertyListRequestSerializer,前者是后两者的父类,这三者的区别为,当请求序列化是AFURLRequestSerialization时,Content-Type=application/x-www-form-urlencoded,body存放的是QueryString;当请求序列化是AFJSONRequestSerializer时,Content-Type=application/json,body存放的是输入参数的JSON格式;当请求是AFPropertyListRequestSerializer时,Content-Type=application/x-plist,body是plist格式的数据。
multipart/form-data请求序列化
首先来看下mulitpart/form-data协议格式:
HTTP Header部分:
需要指定ContentType: multipart/form-data; boundary=Sample-Boundary.
HTTP Body部分:
–Sample-Boundary(换行)
Content-Disposition: form-data; name=”xxx”; filename=”xxx.xx”(换行)
Content-Type :MIME类型(换行)
(再换行)
helloworld(此处正文部分,helloworld表示要传输的内容,换行)
(再换行)
–Sample-Boundary(开始另一部分,内容格式与前面部分一致)
Content-Disposition: form-data; name=”xxx”; filename=”xxx.xx”(换行)
Content-Type :MIME类型(换行)
(再换行)
helloworld(此处正文部分,helloworld表示要传输的内容,换行)
–Sample-Boundary–(结尾)
上述就是Multipart的协议格式,大体来说就是分为3个部分,分别为boundary(–Sample-Boundary,–Sample-Boundary–),header(Content-Disposition: form-data; name=”xxx”; filename=”xxx.xx”,Content-Type :MIME类型),内容(helloworld)。下面就讲下AFNetworking是如果实现上述3部分协议格式的。
Boundary
因为协议中有开始Boundary、中间Boundary、结束Boundary,所以AFNetworking中定义了3个全局的boundary函数(其中boundary是随机生成的):AFMultipartFormInitialBoundary、AFMultipartFormEncapsulationBoundary、AFMultipartFormFinalBoundary
,例如AFMultipartFormInitialBoundary的实现为:
static inline NSString * AFMultipartFormInitialBoundary(NSString *boundary) {
return [NSString stringWithFormat:@"--%@%@", boundary, kAFMultipartFormCRLF];
}
Header
这里的header主要是指Content-Disposition: form-data; name=”xxx”; filename=”xxx.xx”和,Content-Type :MIME类型,AFNetworking在实现multipart/form-data传输时定义了一个叫做AFMultipartFormData的protocol,使用者需要实现该protocol,该protocol的接口类似
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
name:(NSString *)name
error:(NSError * _Nullable __autoreleasing *)error;
从上面的接口就可以确定header了,filename=[fileURL lastPathComponent],name使用者提供,MIME类型是通过fileURL的扩展名计算出来的,具体计算代码如下:
static inline NSString * AFContentTypeForPathExtension(NSString *extension) {
NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL);
NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType);
if (!contentType) {
return @"application/octet-stream";
} else {
return contentType;
}
}
另外AFMultipartFormData protocol也提供了由使用者指定filname、name、MIME type等的接口,具体科参考AFMultipartFormData。
content(内容)
AFNetworking主要是通过NSInputStream来管理输入内容的,具体做法是定义了两个类,一个AFMultipartBodyStream继承自NSInputStream,负责直接对接NSURLRequest的HTTPBodyStream;另一个类是AFHTTPBodyPart,主要负责multipart中具体的某一部分,整体类结构如下图所示:
这里需要注意的是:AFMultipartBodyStream继承自NSInputStream,而NSInputStream是一个abstract类,所以需要自己实现NSInputStream的所有方法;而AFMultipartFormData protocol中每一个接口相当于产生了一个AFHTTPBodyPart,负责该部分的boundary、header、body,这三部分构成了一个大的输入,而这个大的输入是通过NSInputStream输入到AFMultipartBodyStream继而输入到NSURLRequest的HTTPBodyStream中,相关代码如下:
- (NSInputStream *)inputStream {
if (!_inputStream) {
if ([self.body isKindOfClass:[NSData class]]) {
_inputStream = [NSInputStream inputStreamWithData:self.body];
} else if ([self.body isKindOfClass:[NSURL class]]) {
_inputStream = [NSInputStream inputStreamWithURL:self.body];
} else if ([self.body isKindOfClass:[NSInputStream class]]) {
_inputStream = self.body;
} else {
_inputStream = [NSInputStream inputStreamWithData:[NSData data]];
}
}
return _inputStream;
}
本篇完~