博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
AFNetworking之AFURLRequestSerialization深入学习
阅读量:6892 次
发布时间:2019-06-27

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

AFURLRequestSerialization主要是对请求进行编码.

1. AFURLRequestSerialization协议

AFURLRequestSerialization是一个协议, 请求序列化将参数编码为查询字符串、HTTP主体、必要时设置适当的HTTP头字段. AFURLRequestSerialization协议中声明了一个方法:

- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request                               withParameters:(nullable id)parameters                                        error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;复制代码

根据指定的参数parametersrequest进行编码, 并将编码以后的request进行返回.

AFURLRequestSerialization文件中声明了三种序列化器, 分别为: AFHTTPRequestSerializer实现了AFURLRequestSerialization协议, 查询字符串/URL表单编码的参数序列化和默认的请求头,以及响应状态代码和内容类型验证. AFJSONRequestSerializerAFHTTPRequestSerializer的一个子类, 将parameters参数使用NSJSONSerialization序列化为JSON, 并且设置Content-Typeapplication/json. AFPropertyListRequestSerializerAFHTTPRequestSerializer的一个子类, 将parameters参数使用NSPropertyListSerializer序列化为JSON, 并且设置Content-Typeapplication/x-plist.

2. 对外提供的序列化函数

FOUNDATION_EXPORT NSString * AFPercentEscapedStringFromString(NSString *string);FOUNDATION_EXPORT NSString * AFQueryStringFromParameters(NSDictionary *parameters);复制代码

个人理解为对外提供了两个全局函数 AFPercentEscapedStringFromString:将指定的string字符串进行百分号编码. AFQueryStringFromParameters:将指定的parameters字典转换为查询字符串,

3. 在AFN中的使用

我们来看一下AFN是如何使用AFHTTPRequestSerializer进行编码的, 在AFHTTPSessionManagerdataTaskWithHTTPMethod方法中进行构建NSMutableURLRequest的方法如下.

- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method                                       URLString:(NSString *)URLString                                      parameters:(id)parameters                                  uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress                                downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress                                         success:(void (^)(NSURLSessionDataTask *, id))success                                         failure:(void (^)(NSURLSessionDataTask *, NSError *))failure{    NSError *serializationError = nil;    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];      ......}复制代码

self.requestSerializer如果没有特殊设置这里默认的是AFHTTPRequestSerializer, 所以这里我们看一下AFHTTPRequestSerializer的序列化方法.

4. AFHTTPRequestSerializer构建

- (instancetype)init {    self = [super init];    if (!self) {        return nil;    }    self.stringEncoding = NSUTF8StringEncoding;    // 存储请求头的字典    self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary];    // 请求头改变的时候在本队列执行, 并行队列    self.requestHeaderModificationQueue = dispatch_queue_create("requestHeaderModificationQueue", DISPATCH_QUEUE_CONCURRENT);    // Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4    NSMutableArray *acceptLanguagesComponents = [NSMutableArray array];    [[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {        float q = 1.0f - (idx * 0.1f);        [acceptLanguagesComponents addObject:[NSString stringWithFormat:@"%@;q=%0.1g", obj, q]];        *stop = q <= 0.5f;    }];    [self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"];    NSString *userAgent = nil;#if TARGET_OS_IOS    // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43    userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]];#elif TARGET_OS_WATCH    // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43    userAgent = [NSString stringWithFormat:@"%@/%@ (%@; watchOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[WKInterfaceDevice currentDevice] model], [[WKInterfaceDevice currentDevice] systemVersion], [[WKInterfaceDevice currentDevice] screenScale]];#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)    userAgent = [NSString stringWithFormat:@"%@/%@ (Mac OS X %@)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[NSProcessInfo processInfo] operatingSystemVersionString]];#endif    if (userAgent) {        if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) {            NSMutableString *mutableUserAgent = [userAgent mutableCopy];            if (CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, (__bridge CFStringRef)@"Any-Latin; Latin-ASCII; [:^ASCII:] Remove", false)) {                userAgent = mutableUserAgent;            }        }        [self setValue:userAgent forHTTPHeaderField:@"User-Agent"];    }    // HTTP Method Definitions; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html    self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil];    self.mutableObservedChangedKeyPaths = [NSMutableSet set];    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {        if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {            [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];        }    }        return self;}复制代码

AFHTTPRequestSerializer的创建也是很重要的一部分. stringEncoding指定默认的编码方式为UTF8编码.

mutableHTTPRequestHeaders保存了我们修改的请求头的信息, 当对请求头做修改时会在requestHeaderModificationQueue并行队列中执行, 将修改的信息保存在mutableHTTPRequestHeaders字典中, 在调用requestBySerializingRequest对请求编码的时候遍历这个字典, 给request设置header.

acceptLanguagesComponents表示客户端支持的语言. userAgent将客户端的环境通过User-Agent字段传给服务器. 通过AFHTTPRequestSerializerObservedKeyPaths函数获取AFN监听哪些头部字段的变化, 并且提供监听方法, 在监听方法中如果有值改变, 就赋值给mutableHTTPRequestHeaders字典.

5. AFURLRequestSerialization序列化方法

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request                               withParameters:(id)parameters                                        error:(NSError *__autoreleasing *)error{    NSParameterAssert(request);    NSMutableURLRequest *mutableRequest = [request mutableCopy];        // 从自己的head遍历, 如果有值就给request的header赋值    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {        if (![request valueForHTTPHeaderField:field]) {            [mutableRequest setValue:value forHTTPHeaderField:field];        }    }];    // 把各种类型的参数, 转成string    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:                    // 将传入的paramter参数, 用=号链接(key1=name1&key2=name2)                    query = AFQueryStringFromParameters(parameters);                    break;            }        }    }    // HTTPMethodsEncodingParametersInURI, 默认为`GET`, `HEAD`, and `DELETE`    if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {        if (query && query.length > 0) {            // 拼接URL            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;}复制代码

将传入的parameters参数序列化到request中. 先遍历HTTPRequestHeaders字典, 将遍历到的值设置到request请求头中. 如果有自定的queryStringSerialization序列化方式, 就执行自定义, 否则按照默认的方式序列化. 这里调用了AFN的自定义函数AFQueryStringFromParameters进行序列化, 下边会讲解这个函数. 比如我们传进来的参数是@{@"key1" : @"name1", @"key2" : @"name2", @"key3" : @"哈哈"}, 参数会经过百分号编码, 执行AFQueryStringFromParameters序列化之后是key1=name1&key2=name2&key3=%E5%93%88%E5%93%88.

如果request的HTTPMethod是GET, HEAD或者DELETE, 就把经过序列化以后的查询字符串拼接到request的URL中. 否则将查询字符串query设置到request的请求体中, 至此request请求设置完毕.

6.AFURLRequestSerialization中的辅助函数

NSString * AFQueryStringFromParameters(NSDictionary *parameters) {    NSMutableArray *mutablePairs = [NSMutableArray array];    for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {        [mutablePairs addObject:[pair URLEncodedStringValue]];    }    return [mutablePairs componentsJoinedByString:@"&"];}NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {    return AFQueryStringPairsFromKeyAndValue(nil, dictionary);}/*{ @"key1" : @"value1", @"key2" : @"value2" }*//** 对参数进行转码, */NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {    NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];    // 将参数升序进行排序    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];    if ([value isKindOfClass:[NSDictionary class]]) {        NSDictionary *dictionary = value;        // Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries        for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {            id nestedValue = dictionary[nestedKey];            if (nestedValue) {                [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];            }        }    } else if ([value isKindOfClass:[NSArray class]]) {        NSArray *array = value;        for (id nestedValue in array) {            [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];        }    } else if ([value isKindOfClass:[NSSet class]]) {        NSSet *set = value;        for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {            [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];        }    } else {        [mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];    }    return mutableQueryStringComponents;}复制代码

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

你可能感兴趣的文章
【图文详解】Iptables
查看>>
Zabbix-web的中文显示及其乱码问题解决方法
查看>>
Gluster管理命令的总结与归纳
查看>>
FreeNAS如何配置LACP(链路聚合和故障)
查看>>
云场景实践研究第6期:游族网络
查看>>
记录由Equal基础知识引起的内存泄露
查看>>
Android:Sensor传感器
查看>>
Eclipse配置实现定制登录界面
查看>>
NO.1 进入IT世界
查看>>
Exceeded maximum number of retries. Exceeded max scheduling attempts 3 for instance
查看>>
Asp.net mvc 3.0新特性-浅析1
查看>>
Hadoop FSDataInputStream 流定位的例子
查看>>
在OWA页面中,增加忘记密码项
查看>>
Samba文件共享服务(共享脚本 让你工作更轻松)
查看>>
AJAX 学习笔记[三] get 与post 模式的区别
查看>>
MES技术
查看>>
GO语言练习:网络编程 ICMP 示例
查看>>
ios11--UIButton
查看>>
Jq-公告渐隐弹出
查看>>
Windows Forms中通过自定义组件实现统一的数据验证(二)
查看>>