美文网首页
处理请求和响应 AFURLSerialization(三)

处理请求和响应 AFURLSerialization(三)

作者: 林大鹏 | 来源:发表于2017-09-12 19:56 被阅读42次

分析过AFNetworking其实是对NSURLSession的封装,也了解了它是如何发出请求的,在这里我们对发送请求和接收响应的过程进行序列化,这涉及到两个模块:

- AFURLRequestSerialization
- AFURLResponseSerialization

前者主要作用是修改请求(主要是HTTP请求)的头部,提供了一些语义明确的接口设置HTTP头部字段。后者是处理响应的模块,将请求返回的数据解析成对应的格式。

我们首先对AFURLResponseSerializer进行简单介绍,因为这个模块使用在AFURLSessionManager也就是核心类中,而后者AFURLRequestSerialization主要用于AFHTTPSessionManager中,因为它主要用于修改HTTP头部

AFURLResponseSerialization

其实在整个AFNetworking项目中并不存在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

遵循这个协议的类同时也要遵循NSObject,NSSecureCodingNSCopying这三个协议,实现安全编码、拷贝以及objective-C对象的基本行为。

仅看 AFURLResponseSerialization协议对类的要求还是十分简单,返回对特定响应的数据解码后的对象。

在具体了解模块中类的实现之前,先看下小模块的结构:

image.png

模块中所有类都遵循AFURLResponseSerialization协议

AFHTTPResponseSerializer 为模块中最重要的根类。

AFHTTPResponseSerializer

下面我们对模块中最重要的根类的实现进行分析,也就是AFHTTPResponseSerializer。它是在AFURLResponseSerialization模块中最基本的类(因为 AFURLResponseSerialization 只是一个协议)。

初始化
首先是这个类的实例化方法:

+ (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而且没有对接收的内容类型加以限制。

acceptableStatusCodes 设置为从200299之间的状态码,因为只有这些状态码表示获得了有效响应。

验证响应的有效性
AFHTTPResponseSerializer 中方法的实现最长,并且最重要的就是- [AFHTTPResponseSerializer validateResponse: data: error:]

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

这个方法根据在初始化中初始化的属性acceptableContentTypesacceptableStatusCodes 来判断当前响应是否有效。

if ([data length] > 0 && [response URL]) {
    NSMutableDictionary *mutableUserInfo = [@{
                                          NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]],
                                          NSURLErrorFailingURLErrorKey:[response URL],
                                          AFNetworkingOperationFailingURLResponseErrorKey: response,
                                        } mutableCopy];
    if (data) {
        mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
    }

    validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);
}

responseIsValid = NO;

其中第一、二部分的代码非常相似,出现错误时通过AFErrorWithUnderlyingError生成格式化之后的错误,最后设置responseIsValid

NSMutableDictionary *mutableUserInfo = [@{
                               NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
                               NSURLErrorFailingURLErrorKey:[response URL],
                               AFNetworkingOperationFailingURLResponseErrorKey: response,
                       } mutableCopy];

if (data) {
    mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
}

validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);

responseIsValid = NO;

协议的实现

首先是对 AFURLResponseSerialization协议的实现

- (id)responseObjectForResponse:(NSURLResponse *)response
                       data:(NSData *)data
                      error:(NSError *__autoreleasing *)error
{
    [self validateResponse:(NSHTTPURLResponse *)response data:data error:error];

    return data;
}

调用上面的方法对响应进行验证,然后返回数据,实在是没有什么难度。
之后对NSSecureCoding 还有 NSCopying 协议的实现也都是大同小异。

AFJSONResponseSerializer

接下来,看一下 AFJSONResponseSerializer 这个继承自AFHTTPResponseSerializer 类的实现。

初始化方法只是在调用父类的初始化方法之后更新了 acceptableContentTypes 属性:

- (instancetype)init {
    self = [super init];
    if (!self) {
        return nil;
    }

    self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];

    return self;
}

协议的实现

这个类中与父类差别最大的就是对 AFURLResponseSerialization 协议的实现。

- (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;
}
  • 验证请求的有效性

    NSStringEncoding stringEncoding = self.stringEncoding;
      if (response.textEncodingName) {
          CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
        if (encoding != kCFStringEncodingInvalidId) {
            stringEncoding = CFStringConvertEncodingToNSStringEncoding(encoding);
        }
    }
    
  • 解决响应体为空,导致无法进行解析,执行错误回调的问题。

  • 序列化 JSON

     id responseObject = nil;
     NSError *serializationError = nil;
     @autoreleasepool {
         NSString *responseString = [[NSString alloc] initWithData:data encoding:stringEncoding];
         if (responseString && ![responseString isEqualToString:@" "]) {
             // Workaround for a bug in NSJSONSerialization when Unicode character escape codes are used instead of the actual character
             // See http://stackoverflow.com/a/12843465/157142
             data = [responseString dataUsingEncoding:NSUTF8StringEncoding];
    
             if (data) {
                 if ([data length] > 0) {
                     responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
                 } else {
                   return nil;
                 }
             } else {
                 NSDictionary *userInfo = @{
                                    NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"Data failed decoding as a UTF-8 string", @"AFNetworking", nil),
                                    NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Could not decode string: %@", @"AFNetworking", nil), responseString]
                                    };
    
               serializationError = [NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo];
            }
         }
     }
    
  • 移除JSON 中的 null

     if (self.removesKeysWithNullValues && responseObject) {
       responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
     }
    

其中移除JSONnull函数AFJSONObjectByRemovingKeysWithNullValues是一个递归调用的函数:

static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) {
    if ([JSONObject isKindOfClass:[NSArray class]]) {
        NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]];
        for (id value in (NSArray *)JSONObject) {
            [mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)];
        }

        return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray];
    } else if ([JSONObject isKindOfClass:[NSDictionary class]]) {
        NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:JSONObject];
        for (id <NSCopying> key in [(NSDictionary *)JSONObject allKeys]) {
            id value = (NSDictionary *)JSONObject[key];
            if (!value || [value isEqual:[NSNull null]]) {
                [mutableDictionary removeObjectForKey:key];
            } else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {
                mutableDictionary[key] = AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions);
            }
        }

        return (readingOptions & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary];
    }

    return JSONObject;
}

其中 移除 null 靠的就是 [mutableDictionary removeObjectForKey:key] 这一行代码。

AFURLRequestSerialization

AFURLRequestSerialization 的主要工作是对发出的HTTP请求进行处理,它有几部分的工作需要完成。
而这个文件中的大部分类都是为了AFHTTPRequestSerializer 服务的:

  • 处理查询的URL 参数

  • 设置 HTTP 头部字段

  • 设置请求的属性

  • 分块上传

处理查询参数

处理查询参数这部分主要是通过AFQueryStringPair 还有一些C 函数来完成的, 这两个类有两个属性fieldvalue对应HTTP 请求的查询URL中的参数。

@interface AFQueryStringPair : NSObject
@property (readwrite, nonatomic, strong) id field;
@property (readwrite, nonatomic, strong) id value;

- (instancetype)initWithField:(id)field value:(id)value;

- (NSString *)URLEncodedStringValue;
@end

这里- [AFQueryStringPair URLEncodedStringValue]方法会返回 key = value 这种格式, 同时使用 AFPercentEscapedStringFromString 函数来对fieldvalue 进行处理, 将其中的:#[]@!$&'()*+,;=等字符转换为百分号表示的形式。

同时还负责返回查询参数, 将 AFQueryStringPair或者 key value 转换为以下这种形式。

username=dravenss&password=123456&hello[world]=helloworld

它的实现主要依赖于一个递归函数AFQueryStringPairsFromKeyAndValue ,如果当前的value 是一个集合类型的话,那么它就会不断的递归调用自己。

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

最后返回一个数组:

[
  username=draveness,
  password=123456,
  hello[world]=helloworld
]

得到这个数组之后就会调用 AFQueryStringFromParameters来使用 &来拼接它们。

static NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
   NSMutableArray *mutablePairs = [NSMutableArray array];
    for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
        [mutablePairs addObject:[pair URLEncodedStringValue]];
    }

    return [mutablePairs componentsJoinedByString:@"&"];
}

设置HTTP头部字段

AFHTTPRequestSerializer 在头文件中提供一些属性方便我们设置HTTP头部字段。同时,在类的内部,它提供了-[AFHTTPRequestSerializer setValue: forHTTPHeaderField:]方法来设置HTTP头部,其实它的实现都是基于一个名为mutableHTTPRequestHeaders属性的:

- (void)setValue:(NSString *)value
forHTTPHeaderField:(NSString *)field
{
    [self.mutableHTTPRequestHeaders setValue:value forKey:field];
}

- (NSString *)valueForHTTPHeaderField:(NSString *)field {
    return [self.mutableHTTPRequestHeaders valueForKey:field];
}

在设置HTTP头部字段时,都会存储到这个可变字典中,而当真正使用时,会用 HTTPRequestHeaders 这个方法,来获取对应版本的不可变字典。

- (NSDictionary *)HTTPRequestHeaders {
    return [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders];
}

到了这里,可以来分析一下,这个类是如何设置一些我们常用的头部字段的。首先是User-Agent,在AFHTTPRequestSerializer刚刚初始化时,就会根据当前编译的平台生成一个 userAgent 字符串:

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

[self setValue:userAgent forHTTPHeaderField:@"User-Agent"];

设置请求的属性

还有一些 NSURLRequest的属性是通过另一种方式来设置的,AFNetworking 为这些功能提供了接口

@property (nonatomic, assign) BOOL allowsCellularAccess;

@property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy;

@property (nonatomic, assign) BOOL HTTPShouldHandleCookies;

@property (nonatomic, assign) BOOL HTTPShouldUsePipelining;

@property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType;

@property (nonatomic, assign) NSTimeInterval timeoutInterval;

它们都会通过AFHTTPRequestSerializerObsservedKeyPaths的调用而返回:

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

在这些属性被设置时,会触发KVO,然后将新的属性存储在一个名为 mutableObservedChangedKeyPaths 的字典中:

- (void)observeValueForKeyPath:(NSString *)keyPath
                  ofObject:(__unused id)object
                    change:(NSDictionary *)change
                   context:(void *)context
{
    if (context == AFHTTPRequestSerializerObserverContext) {
        if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
           [self.mutableObservedChangedKeyPaths removeObject:keyPath];
        } else {
            [self.mutableObservedChangedKeyPaths addObject:keyPath];
        }
    }
}

然后在生成 NSURLRequest 的时候设置这些属性:

NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
mutableRequest.HTTPMethod = method;

for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
    if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
        [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
    }
}

工作流程

AFHTTPRequestSerializer会在 AHHTTPSessionManager 初始化是一并初始化,这时它会根据当前系统环境预设置一些HTTP头部字段 Accept-Language User-Agent

- (instancetype)init {
    self = [super init];
    if (!self) {
        return nil;
    }

    self.stringEncoding = NSUTF8StringEncoding;

    self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary];


    #1: 设置接收语言,用户代理,略

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

同时它还对一些属性进行 KVO,确保它们在改变后更新 NSMutableURLRequest 中对应的属性。

在初始化之后,如果调用了 - [AFHTTPSessionManager dataTaskWithHTTPMethod:URLString:parameters:uploadProgress:downloadProgress:success:failure:],就会进入 AFHTTPRequestSerializer 的这一方法:

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                             URLString:(NSString *)URLString
                            parameters:(id)parameters
                                 error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(method);
    NSParameterAssert(URLString);

    NSURL *url = [NSURL URLWithString:URLString];

    NSParameterAssert(url);

    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
    mutableRequest.HTTPMethod = method;

    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
            [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
        }
    }

    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];

    return mutableRequest;
}
  • 对参赛进行检查

  • 设置HTTP方法

    mutableRequest.HTTPMethod = method;
    
  • 通过mutableObservedChangedKeyPaths字典设置 NSMutableURLRequest属性

      for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
          if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
              [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
          }
      }
    
  • 调用 - [AFHTTPRequestSerializer requestBySerializingRequest:withParameters:error:] 设置 HTTP 头部字段和查询参数。

    - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                             withParameters:(id)parameters
                                      error:(NSError *__autoreleasing *)error
    {
        NSParameterAssert(request);
    
        NSMutableURLRequest *mutableRequest = [request mutableCopy];
    
        [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) {
                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;
    }
    
  • 通过 HTTPRequestHeaders 字典设置头部字段

    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
      if (![request valueForHTTPHeaderField:field]) {
          [mutableRequest setValue:value forHTTPHeaderField:field];
      }
    

    }];

  • 调用 AFQueryStringFromParameters 将参数转换为查询参数。

    query = AFQueryStringFromParameters(parameters);
    
  • parameters添加到 URL 或者 HTTP body 中。

    if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
        if (query) {
            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]];
    }
    
    • 如果HTTP 方法为 GET HEAD 或者DELETE,也就是在初始化方法中设置的,那么参数会追加到URL后面。否则会被放入 HTTP body 中。
  • 最后该方法会返回一个 NSMutableURLRequest

小结

- AFURLResponseSerialization 负责对返回的数据进行序列化
- AFURLRequestSerialization 负责生成 NSMutableURLRequest,为请求设置 HTTP头部,管理发出的请求

相关文章

  • 处理请求和响应 AFURLSerialization(三)

    分析过AFNetworking其实是对NSURLSession的封装,也了解了它是如何发出请求的,在这里我们对发送...

  • 爬虫——进阶(一)requests

    一:requests概述 requests第三方封装的模块,通过简化请求和响应数据的处理,简化繁琐的开发步骤和处理...

  • HTTP -- 事务的延迟

    DNS查询 - 连接 - 请求 - 处理 - 响应 - 关闭 与建立TCP连接,以及传输请求和响应报文的时间相比,...

  • .Net Core 3.x MVC 了解中间件

    ASP.NET Core 中的中间件是嵌入到应用管道中用于处理请求和响应的一段代码。 ASP.NET Core 请...

  • header响应头

    什么是header HTTP协议的请求和响应报文中必定包含HTTP首部。首部内容为客户端和服务器分别处理请求和响应...

  • 过滤器、拦截器、切片区别

    过滤器:可以拿到原始的HTTP请求和响应信息,拿不到处理请求的方法值信息 拦截器:既可以拿到HTTP请求和响应信息...

  • 《图解HTTP》笔记概要2

    HTTP首部 HTTP协议的请求和响应报文中必定包含HTTP首部。首部内容为客户端和服务器分别处理请求和响应所提供...

  • 爬虫 0030~ requests利刃出鞘

    1-1 简介 requests第三方封装的模块,通过简化请求和响应数据的处理,简化繁琐的开发步骤和处理逻辑、统一...

  • HTTP 报文首部(一)

    报文首部 HTTP 协议的请求和响应报文中必定包含 HTTP 首部。首部内容为客户端和服务器分别处理请求和响应提供...

  • 《图解HTTP》读书笔记4之HTTP首部

    一,HTTP报文首部HTTP协议的请求和响应报文中必定包含HTTP首部。首部内容为客户端和服务器分别处理请求和响应...

网友评论

      本文标题:处理请求和响应 AFURLSerialization(三)

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