美文网首页
AFNetworking之AFURLRequestSeriali

AFNetworking之AFURLRequestSeriali

作者: OneAlon | 来源:发表于2018-01-06 14:22 被阅读187次

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

    字符转码
    当发送的网络请求URL中包含了特殊字符, 造成URL无法被解析时, 那这个请求就会发送失败, 出现问题.
    所以在发送网络请求之前, 需要对URL进行编码, 对特殊字符进行处理. RFC 3986规定URL中允许包含的字符有英文字母(a~z A~Z) 数字(0~9)以及下列保留字符等.
    RFC 3986中定义的保留字符有

     - General Delimiters: ":", "#", "[", "]", "@", "?", "/"
     - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "="
    

    URL的编码通常采用百分号编码, 百分号编码是使用% + (0123456789ABCDEF)中的两位代表一个字符的十六进制形式, URL的编码字符集是US-ASCII, 比如a的ASCII是0x61, 那url编码以后就是%61. 再比如abc经过URL编码以后就是%61%62%63.

    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请求设置完毕.

    application/x-www-form-urlencoded:数据被编码为名称/值对。这是标准的编码格式。(默认使用这种格式) multipart/form-data: 数据被编码为一条消息,页上的每个控件对应消息中的一个部分。(上传文件需要用到这种) text/plain: 数据以纯文本形式(text/json/xml/html)进行编码,其中不含任何控件或格式字符。postman软件里标的是RAW。

    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;
    }
    

    相关文章

      网友评论

          本文标题:AFNetworking之AFURLRequestSeriali

          本文链接:https://www.haomeiwen.com/subject/knjynxtx.html