博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
处理请求(AFURLRequestSerialization)和响应(AFURLResponseSerialization)
阅读量:6278 次
发布时间:2019-06-22

本文共 8803 字,大约阅读时间需要 29 分钟。

处理请求(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件事:

  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 
- (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 之间的状态码, 因为只有这些状态码表示获得了有效的响应

验证响应的有效性

- (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;}复制代码

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

转载地址:http://rjfva.baihongyu.com/

你可能感兴趣的文章
Python系语言发展综述
查看>>
新手 开博
查看>>
借助开源工具高效完成Java应用的运行分析
查看>>
163 yum
查看>>
第三章:Shiro的配置——深入浅出学Shiro细粒度权限开发框架
查看>>
80后创业的经验谈(转,朴实但实用!推荐)
查看>>
让Windows图片查看器和windows资源管理器显示WebP格式
查看>>
我的友情链接
查看>>
我的友情链接
查看>>
vim使用点滴
查看>>
embedded linux学习中几个需要明确的概念
查看>>
mysql常用语法
查看>>
Morris ajax
查看>>
【Docker学习笔记(四)】通过Nginx镜像快速搭建静态网站
查看>>
ORA-12514: TNS: 监听程序当前无法识别连接描述符中请求的服务
查看>>
<转>云主机配置OpenStack使用spice的方法
查看>>
java jvm GC 各个区内存参数设置
查看>>
[使用帮助] PHPCMS V9内容模块PC标签调用说明
查看>>
关于FreeBSD的CVSROOT的配置
查看>>
基于RBAC权限管理
查看>>