处理请求(AFURLRequestSerialization)和响应(AFURLResponseSerialization)
在这篇文章里,我们将分析一下发出请求以及接受响应
的过程,这部分内容主要涉及以下两个模块:
前者的主要作用处理请求所需的参数(主要是 HTTP 请求),最终得到请求网络需要的NSMutableURLRequest实例。而后者是处理响应的模块,将请求返回的数据解析成对应的格式。 我们首先对AFURLRequestSerialization
进行分析,应为它是一个请求的开始。
AFURLRequestSerialization
在具体了解这个模块的具体实现之前,我们先看一下在这个模块的结构(我觉得这样可以更好的去分析它):AFURLRequestSerialization
并不是一个类,它是一个协议,协议的内容很简单,只有一个必须实现的方法。
@protocol AFURLRequestSerialization- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(nullable id)parameters error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW; @end复制代码
上图中的所有类都遵循AFURLRequestSerialization
协议,AFHTTPRequestSerializer
是模块中的最重要的基类。
AFURLRequestSerialization
的主要工作是对发出的 HTTP 请求进行处理,最终返回NSMutableURLRequest
实例。 接下来,我们看看基类AFHTTPRequestSerializer
做了什么。
AFHTTPRequestSerializer
初始化
首先是这个类的实例化方法:
+ (instancetype)serializer { return [[self alloc] init];}- (instancetype)init { self = [super init]; if (!self) { return nil; } //字符串编码 self.stringEncoding = NSUTF8StringEncoding; //HTTP请求头 self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary]; //设置默认的Accept-Language请求头 ....... //设置默认的User-Agent请求头 ...... //对一些字段添加KVO(timeoutInterval,cachePolicy,...) ...... return self;}复制代码
获取NSMutableURLRequest
实例
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(id)parameters error:(NSError *__autoreleasing *)error{ //断言,debug模式下,如果缺少改参数,crash NSParameterAssert(method); NSParameterAssert(URLString); NSURL *url = [NSURL URLWithString:URLString]; NSParameterAssert(url); NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url]; mutableRequest.HTTPMethod = method; //将request的各种属性循环遍历 for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) { //如果自己观察到的发生变化的属性,在这些方法里 if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) { //把给自己设置的属性给request设置 [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath]; } } //将传入的parameters进行编码,并添加到request中 mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy]; return mutableRequest;}复制代码
这个方法做了3件事:
- 设置request的请求类型,get,post,put...等
- 往request里添加一些参数设置,其中
AFHTTPRequestSerializerObservedKeyPaths()
是一个c函数,返回一个数组,我们来看看这个函数:
static NSArray * AFHTTPRequestSerializerObservedKeyPaths() { static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _AFHTTPRequestSerializerObservedKeyPaths = @[ NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))]; }); return _AFHTTPRequestSerializerObservedKeyPaths;}复制代码
其实这个函数就是封装了一些属性的名字,这些都是NSUrlRequest的属性。 3. 把传进来的参数进行编码,然后放到我们请求的request中。
AFURLRequestSerialization
协议实现
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(id)parameters error:(NSError *__autoreleasing *)error{ NSParameterAssert(request); NSMutableURLRequest *mutableRequest = [request mutableCopy]; //遍历请求头(HTTP head),如果有值,就放入request的head中 [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) { if (![request valueForHTTPHeaderField:field]) { [mutableRequest setValue:value forHTTPHeaderField:field]; } }]; NSString *query = nil; if (parameters) { //自定义的解析方式 if (self.queryStringSerialization) { NSError *serializationError; query = self.queryStringSerialization(request, parameters, &serializationError); if (serializationError) { if (error) { *error = serializationError; } return nil; } } else { //默认解析方式 switch (self.queryStringSerializationStyle) { case AFHTTPRequestQueryStringDefaultStyle: query = AFQueryStringFromParameters(parameters); break; } } } if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) { if (query && query.length > 0) { mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]]; } } else { // #2864: an empty string is a valid x-www-form-urlencoded payload if (!query) { query = @""; } if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) { [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; } [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]]; } return mutableRequest;}复制代码
这个方法做了3件事:
- 将
self.HTTPRequestHeaders
中设置的参数,赋值要请求的request里去。 - 把请求参数,从 array,dic,set 这些容器类型转换为字符串,我们可以使自定义的方式,也可以使用AF默认的。详细代码可以去查看这个方法
NSString * AFQueryStringFromParameters(NSDictionary *parameters)
。 - 根据该request中请求类型来判断参数字符串应该如何设置到request中去。如果是GET、HEAD、DELETE,则把参数quey是拼接到url后面的。而POST、PUT是把query拼接到http body中。
至此,我们得到了一个我们想要的request。
AFHTTPRequestSerializer
,AFJSONRequestSerializer
,AFPropertyListRequestSerializer
这三个类的主要区别在于协议的实现的不同,主要体现在请求头Content-Type
的不同。 AFHTTPRequestSerializer :application/x-www-form-urlencoded AFJSONRequestSerializer :application/json AFPropertyListRequestSerializer : application/x-plist
AFURLResponseSerialization
AFURLResponseSerialization
并不是一个类,它是一个协议,协议的内容很简单,只有一个必须实现的方法。
@protocol AFURLResponseSerialization- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response data:(nullable NSData *)data error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;@end复制代码
AFHTTPResponseSerializer
初始化
+ (instancetype)serializer { return [[self alloc] init];}- (instancetype)init { self = [super init]; if (!self) { return nil; } self.stringEncoding = NSUTF8StringEncoding; self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)]; self.acceptableContentTypes = nil; return self;}复制代码
因为是对 HTTP 响应进行序列化,所以这里设置了 stringEncoding
为 NSUTF8StringEncoding
而且没有对self.acceptableContentTypes
(接收的内容类型)加以限制。 将 acceptableStatusCodes
设置为从 200
到 299
之间的状态码, 因为只有这些状态码表示获得了有效的响应
。
验证响应的有效性
- (BOOL)validateResponse:(NSHTTPURLResponse *)response data:(NSData *)data error:(NSError * __autoreleasing *)error{ BOOL responseIsValid = YES; NSError *validationError = nil; if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) { if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]]) { #1: 返回内容类型无效 } if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) { #2: 返回状态码无效 } } if (error && !responseIsValid) { *error = validationError; } return responseIsValid;}复制代码
AFURLResponseSerialization 协议的实现
- (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error{ [self validateResponse:(NSHTTPURLResponse *)response data:data error:error]; return data;}复制代码
调用上面的方法对响应进行验证,然后返回二进制数据。
AFJSONResponseSerializer
AFJSONResponseSerializer
这个继承自 AFHTTPResponseSerializer
类的实现。
- (instancetype)init { self = [super init]; if (!self) { return nil; } self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil]; return self;}复制代码
从上面我们可以看出,在调用玩父类的初始化方法后,更新了 acceptableContentTypes
属性。
协议的实现
- (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error{ #1: 验证请求 #2: 解决一个由只包含一个空格的响应引起的 bug, 略 #3: 序列化 JSON #4: 移除 JSON 中的 null if (error) { *error = AFErrorWithUnderlyingError(serializationError, *error); } return responseObject;}复制代码
至于剩下的类大家可以去看源码的实现,其实差别不大,只是协议的实现不同而已。