处理请求(AFURLRequestSerialization)和响应(AFURLResponseSerialization)

处理请求(AFURLRequestSerialization)和响应(AFURLResponseSerialization)

在这篇文章里,我们将分析一下发出请求以及接受响应的过程,这部分内容主要涉及以下两个模块:

  1. AFURLRequestSerialization
  2. 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件事:

  1. 设置request的请求类型,get,post,put...等
  2. 往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件事:

  1. self.HTTPRequestHeaders中设置的参数,赋值要请求的request里去。
  2. 把请求参数,从 array,dic,set 这些容器类型转换为字符串,我们可以使自定义的方式,也可以使用AF默认的。详细代码可以去查看这个方法NSString * AFQueryStringFromParameters(NSDictionary *parameters)
  3. 根据该request中请求类型来判断参数字符串应该如何设置到request中去。如果是GET、HEAD、DELETE,则把参数quey是拼接到url后面的。而POST、PUT是把query拼接到http body中。

至此,我们得到了一个我们想要的request。

AFHTTPRequestSerializerAFJSONRequestSerializerAFPropertyListRequestSerializer这三个类的主要区别在于协议的实现的不同,主要体现在请求头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 响应进行序列化,所以这里设置了 stringEncodingNSUTF8StringEncoding 而且没有对self.acceptableContentTypes(接收的内容类型)加以限制。 将 acceptableStatusCodes 设置为从 200299 之间的状态码, 因为只有这些状态码表示获得了有效的响应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;
}
复制代码

至于剩下的类大家可以去看源码的实现,其实差别不大,只是协议的实现不同而已。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值