知识预备:
- Content-Type:表示具体请求中或者返回的数据类型
application/x-www-urlencoded
:form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)multipart/form-data
:文件上传时application/json
:JSON数据格式application/octet-stream
: 二进制流数据(如常见的文件下载)
-
form-data
,multipart/form-data
主要用于 POST方法中传递多种格式和含义的数据,在 body 中引入 boundary 的概念,用分割线将多部分数据融合到一个 body 中发送给服务端。那么对于一个简单的 form-data,它发送的 body 内容可能如下
--Boundary+FD2E180F039993ED
Content-Disposition: form-data; name="myArray[]"
v1
--Boundary+FD2E180F039993ED
Content-Disposition: form-data; name="myArray[]"
v2
--Boundary+FD2E180F039993ED
Content-Disposition: form-data; name="myArray[]"
v3
--Boundary+FD2E180F039993ED
Content-Disposition: form-data; name="mydic[key1]"
value1
--Boundary+FD2E180F039993ED
Content-Disposition: form-data; name="mydic[key2]"
value2
--Boundary+FD2E180F039993ED
header: headerkey
BodyData
--Boundary+FD2E180F039993ED--
它的特点是
- 每一部分都可以包含 header,一般默认必须包含的标识 header 是 Content-Disposition
- 头部和每一部分需要以 --Boundary+{XXX} 格式分割
- 末尾以 --Boundary+{XXX}-- 结束
- 请求头中,要设置 Content-Type: multipart/form-data; boundary=Boundary+{XXX}
- 请求头要设置 Content-Length 为 body 总长度
一般的请求创建方法:
- (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;
}
主要是调用requestBySerializingRequest
方法,设置一个默认的序列化请求
- (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) {
//自定义的序列化方法block
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;
}
}
}
//不过请求方式是@"GET", @"HEAD", @"DELETE",就需要进行拼接
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;
}
如果是上传文件的请求,那就用multipartFormRequestWithMethod
这个方法,它对头部的处理又会有一些不同,会设置上传文件所需要的一些头部信息,比如Content-Disposition,Content-Type, Content-Length,分隔符Boundary
- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(NSDictionary *)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(method);
NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]);
NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error];
//AFStreamingMultipartFormData做的事情就是设置文件上传需要的头部信息,如Content-Disposition,Content-Type, Content-Length,分隔符Boundary
__block AFStreamingMultipartFormData *formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding];
if (parameters) {
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
NSData *data = nil;
if ([pair.value isKindOfClass:[NSData class]]) {
data = pair.value;
} else if ([pair.value isEqual:[NSNull null]]) {
data = [NSData data];
} else {
data = [[pair.value description] dataUsingEncoding:self.stringEncoding];
}
if (data) {
[formData appendPartWithFormData:data name:[pair.field description]];
}
}
}
if (block) {
block(formData);
}
return [formData requestByFinalizingMultipartFormData];
}
如果请求数据是JSON格式数据,其实他的序列化工作只是把Content-Type改为application/json,把json序列化数据设置成HTTPbody,同理以属性列表的方式请求也是一样,把Content-Type改为application/x-plist,然后把属性列表序列化数据设置成HTTPbody
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(request);
//对于`GET`,`HEAD`,`DELETE`等方法中。直接使用父类的处理方式
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
return [super requestBySerializingRequest:request withParameters:parameters error:error];
}
NSMutableURLRequest *mutableRequest = [request mutableCopy];
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
if (parameters) {
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
}
if (![NSJSONSerialization isValidJSONObject:parameters]) {
if (error) {
NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"The `parameters` argument is not valid JSON.", @"AFNetworking", nil)};
*error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo];
}
return nil;
}
//把parameters转换为JSON序列化的data
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error];
if (!jsonData) {
return nil;
}
[mutableRequest setHTTPBody:jsonData];
}
return mutableRequest;
}
总结
AFURLRequestSerialization核心就是根据不同的请求类型,设置不同的请求头信息,选择对应的方法序列化请求数据然后设置成HTTPbody