处理请求(AFURLRequestSerialization)和响应(AFURLResponseSerialization)
在这篇文章里,我们将分析一下发出请求以及接受响应
的过程,这部分内容主要涉及以下两个模块:
前者的主要作用处理请求所需的参数(主要是 HTTP 请求),最终得到请求网络需要的NSMutableURLRequest实例。而后者是处理响应的模块,将请求返回的数据解析成对应的格式。 我们首先对AFURLRequestSerialization
进行分析,应为它是一个请求的开始。
AFURLRequestSerialization
在具体了解这个模块的具体实现之前,我们先看一下在这个模块的结构(我觉得这样可以更好的去分析它):AFURLRequestSerialization
并不是一个类,它是一个协议,协议的内容很简单,只有一个必须实现的方法。
@protocol AFURLRequestSerialization <NSObject, NSSecureCoding, NSCopying>
- (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 <NSObject, NSSecureCoding, NSCopying>
- (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
之间的状态码, 因为只有这些状态码表示获得了有效的响应
。HTTP code
验证响应的有效性
- (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;
}
复制代码
至于剩下的类大家可以去看源码的实现,其实差别不大,只是协议的实现不同而已。