AFNetworking之HTTP请求的序列化

AFURLRequestSerialization协议

AFURLRequestSerialization协议 是所有参数编码器的抽象协议,此协议只有一个方法.

@protocol AFURLRequestSerialization 
/**将参数编码到原请求的copy中并返回*/
- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(nullable id)parameters
                                        error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
@end

AFHTTPRequestSerializer

AFHTTPRequestSerializer是用来专门序列化HTTP请求的类.主要职责是创建经过HTTP参数序列化构造的 NSMutableRequest

AFHTTPRequestSerializer接口

@interface AFHTTPRequestSerializer:: NSObject 
/**
文本的编码方案,默认NSUTF8StringEncoding
 */
@property (nonatomic, assign) NSStringEncoding stringEncoding;

/**
是否允许使用蜂窝网,默认YES
 */
@property (nonatomic, assign) BOOL allowsCellularAccess;

/**
缓存策略,默认YES
 */
@property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy;
/**
是否使用默认的cookies 处理,默认YES
 */
@property (nonatomic, assign) BOOL HTTPShouldHandleCookies;

/**
 在接收到相应之前是否可以继续发送数据,默认NO
 */
@property (nonatomic, assign) BOOL HTTPShouldUsePipelining;

/**
请求的网络服务类型,默认NSURLNetworkServiceTypeDefault
 */
@property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType;

/**
请求的超时时间,默认60 seconds.
 */
@property (nonatomic, assign) NSTimeInterval timeoutInterval;

/**
序列化请求的默认HTTP头域值,默认包含:
 `Accept-Language`  内容是 `NSLocale +preferredLanguages`,
`User-Agent`  内容是操作系统名称和各种包id
添加和移除头信息使用 `setValue:forHTTPHeaderField:`方法
*/
@property (readonly, nonatomic, strong) NSDictionary  *HTTPRequestHeaders;

/**
实例化类方法
 */
+ (instancetype)serializer;

/**
设置 HTTP 头信息,如果为'nil',则移除已经存在的条目
 */
- (void)setValue:(nullable NSString *)value
forHTTPHeaderField:(NSString *)field;

/**
获取HTTP headers中的信息
 */
- (nullable NSString *)valueForHTTPHeaderField:(NSString *)field;

/**
设置 有 HTTP 客户端制造的 认证 HTTP头信息 ,使用 base64编码的基础用户名和密码认证.
 */
- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username
                                       password:(NSString *)password;

/**
清除已经存在的认证HTTP头信息.
 */
- (void)clearAuthorizationHeader;

/**
序列化后的请求会编码参数作为查询串的方法,默认`GET`, `HEAD`, and `DELETE`
*/
@property (nonatomic, strong) NSSet  *HTTPMethodsEncodingParametersInURI;

/**
设置query string 序列化的方式
 */
- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style;

/*
设置序列化block,用来自定义序列化query string
*/
- (void)setQueryStringSerializationWithBlock:(nullable NSString * (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block;

/**
使用指定的 HTTP方法和URL 创建 NSMutableURLRequest,如果HTTP是 `GET`, `HEAD`, or `DELETE`,参数会被编码为 query string 附加到url上.否则 参数根据 parameterEncoding 属性进行编码 放到 请求体中.
 */
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(nullable id)parameters
                                     error:(NSError * _Nullable __autoreleasing *)error;
/**
使用指定的 HTTP方法和URL 创建 NSMutableURLRequest,并且构造 `multipart/form-data` HTTP 请求体,使用指定的参数和 多部分表单数据block
多表单请求是自动流式传输,直接从磁盘读取文件和单个HTTPbody中的在内存中的数据,作为结果的 NSMutableURLRequest对象有个HTTPBodyStream 属性,所以不要设置它的 HTTPBodyStream 或者  HTTPBody属性,那会清除多表单体流.
 */
- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
                                              URLString:(NSString *)URLString
                                             parameters:(nullable NSDictionary  *)parameters
                              constructingBodyWithBlock:(nullable void (^)(id  formData))block
                                                  error:(NSError * _Nullable __autoreleasing *)error;

/**
移除 request 中的 HTTPBodyStream 创建一个NSMutableURLRequest并返回.,将HTTPBodyStream中的内容异步地写它的内容到指定的文件中,在完成时调用 completion handler .
这样做的目的是为了解决`NSURLSessionTask`在设置流内容消息体后不发送Content-length的问题(尤其是与Amaza S3 webservice通信时),被写入后的文件可以被用来传递给`AFURLSessionManager -uploadTaskWithRequest:fromFile:progress:completionHandler:` 或者 重新读取为`NSData`设置到 HttpBody中.
 */
- (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request
                             writingStreamContentsToFile:(NSURL *)fileURL
                                       completionHandler:(nullable void (^)(NSError * _Nullable error))handler;
@end

创建序列化参数到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];
        }
    }
    //这里传递了parameters的值会将 参数序列化为url encode的形式放到消息体中
    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];

    return mutableRequest;
}

- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
                                              URLString:(NSString *)URLString
                                             parameters:(NSDictionary *)parameters
                              constructingBodyWithBlock:(void (^)(id  formData))block
                                                  error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(method);
    //GET和 HEAD 方法不能将参数序列化到 HTTP body中
    NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]);

    //获得了设置了头信息, parameters 参数传递了nil 所以请求体中没有参数
    NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error];

    //创建多部分表单数据
    __block AFStreamingMultipartFormData *formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding];

    if (parameters) {
        //遍历所有的参数值,将序列化好的 key value 添加在formData 中
        for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
           //将parameters 中的value 转化为二进制
            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];
            }
            //将某一数据添加到form data中
            if (data) {
                [formData appendPartWithFormData:data name:[pair.field description]];
            }
        }
    }
    //调用外界构造http body 闭包
    if (block) {
        block(formData);
    }

    //返回最终的 NSMutableURLRequest
    return [formData requestByFinalizingMultipartFormData];
}

- (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request
                             writingStreamContentsToFile:(NSURL *)fileURL
                                       completionHandler:(void (^)(NSError *error))handler
{
    //参数合理性判断
    NSParameterAssert(request.HTTPBodyStream);
    NSParameterAssert([fileURL isFileURL]);
    
    //获取出请求中的 HTTPBodayStream
    NSInputStream *inputStream = request.HTTPBodyStream;
    //创建使用文件路径 OuputStream
    NSOutputStream *outputStream = [[NSOutputStream alloc] initWithURL:fileURL append:NO];
    __block NSError *error = nil;
  
    //全局队列异步执行
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //将读取流与写入流添加到 run loop中
        [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

        [inputStream open];
        [outputStream open];

        while ([inputStream hasBytesAvailable] && [outputStream hasSpaceAvailable]) {
            //创建大小为1K的缓冲区
            uint8_t buffer[1024];
            //读取数据到buffer中
            NSInteger bytesRead = [inputStream read:buffer maxLength:1024];
            //读取发生错误则返回失败
            if (inputStream.streamError || bytesRead < 0) {
                error = inputStream.streamError;
                break;
            }
            //将buffer中的数据写入文件
            NSInteger bytesWritten = [outputStream write:buffer maxLength:(NSUInteger)bytesRead];
            //发生错误则直接返回
            if (outputStream.streamError || bytesWritten < 0) {
                error = outputStream.streamError;
                break;
            }
            
            //没有数据可以读写跳出循环
            if (bytesRead == 0 && bytesWritten == 0) {
                break;
            }
        }

        //关闭流
        [outputStream close];
        [inputStream close];

        if (handler) {
            //mian queue 异步调用完成回调
            dispatch_async(dispatch_get_main_queue(), ^{
                handler(error);
            });
        }
    });
    
    //拷贝原请求并清空 HTTPBodyStream
    NSMutableURLRequest *mutableRequest = [request mutableCopy];
    mutableRequest.HTTPBodyStream = nil;

    return mutableRequest;
}

请求序列化之 AFMultipartFormData 协议

AFMultipartFormData 协议定义了构造多表单每个条目的接口

@protocol AFMultipartFormData
/**
添加HTTP头 Content-Disposition: file; filename=#{generated filename}; name=#{name} 和 Content-Type: #{generated mimeType},后面根 编码过后的数据,和多表单边界.
数据的filename 和 MIME type会 使用fileURL的最后一个部分和系统相关的MIME type 自动生成,
 */
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
                         name:(NSString *)name
                        error:(NSError * _Nullable __autoreleasing *)error;

/*添加HTTP头 Content-Disposition: file; filename=#{generated filename}; name=#{name} 和 Content-Type: #{generated mimeType},后面根 编码过后的数据,和多表单边界. 
此方法是可以设置mimeType版本
*/
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
                         name:(NSString *)name
                     fileName:(NSString *)fileName
                     mimeType:(NSString *)mimeType
                        error:(NSError * _Nullable __autoreleasing *)error;
/*
使用 inputStream 添加HTTP表单信息
*/
- (void)appendPartWithInputStream:(nullable NSInputStream *)inputStream
                             name:(NSString *)name
                         fileName:(NSString *)fileName
                           length:(int64_t)length
                         mimeType:(NSString *)mimeType;
/*
使用NSData 构建HTTP表单信息
*/
- (void)appendPartWithFileData:(NSData *)data
                          name:(NSString *)name
                      fileName:(NSString *)fileName
                      mimeType:(NSString *)mimeType;
- (void)appendPartWithFormData:(NSData *)data
                          name:(NSString *)name;
/*
使用自定义的头和body构建HTTP表单信息
*/
- (void)appendPartWithHeaders:(nullable NSDictionary  *)headers
                         body:(NSData *)body;

/*
通过限制数据包大小和为从上载流读取的每个数据块添加延迟来限制请求带宽。
当通过3G或边缘连接上载时,请求可能会失败,并显示“请求正文流已耗尽”。根据推荐值设置最大包大小和延迟(‘KAFPuxAdvRAD3GGGGESTEDPACKETSIZE’和‘KAFPuxAdRead 3GugGeGestDeld'’)降低了输入流超过其分配带宽的风险。不幸的是,在“NSURLConnection”上没有明确的方法来区分3G、EDGE或LTE连接。因此,不建议仅基于网络可达性来限制带宽。相反,您应该考虑检查故障块中的“请求正文流已耗尽”,然后使用带宽限制重试请求。


PARAM数字节最大包大小,字节数。输入流的默认数据包大小为16kb。

@参数每次读取数据包时的延迟持续时间。默认情况下,不设置延迟。
*/
- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes
                                  delay:(NSTimeInterval)delay;
@end

AFStreamingMultipartFormData

AFStreamingMultipartFormData是对HTTP请求多表单数据的抽象,实现了 AFMultipartFormData协议封装了HTTP 每个表单条目的构造过程.

@interface AFStreamingMultipartFormData : NSObject 
//使用 request 和 编码方案 创建数据
- (instancetype)initWithURLRequest:(NSMutableURLRequest *)urlRequest
                    stringEncoding:(NSStringEncoding)encoding;
//得到最终的带多表单数据的请求
- (NSMutableURLRequest *)requestByFinalizingMultipartFormData;
@end

@implementation AFStreamingMultipartFormData
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
                         name:(NSString *)name
                     fileName:(NSString *)fileName
                     mimeType:(NSString *)mimeType
                        error:(NSError * __autoreleasing *)error
{
    NSParameterAssert(fileURL);
    NSParameterAssert(name);
    NSParameterAssert(fileName);
    NSParameterAssert(mimeType);
    //当URL 不是 file url 的情况 返回失败
    if (![fileURL isFileURL]) {
        NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"Expected URL to be a file URL", @"AFNetworking", nil)};
        if (error) {
            *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo];
        }

        return NO;
        //文件不能被获取返回失败
    } else if ([fileURL checkResourceIsReachableAndReturnError:error] == NO) {
        NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"File URL not reachable.", @"AFNetworking", nil)};
        if (error) {
            *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo];
        }

        return NO;
    }

    //获取文件信息
    NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:error];
    if (!fileAttributes) {
        return NO;
    }
    
    //构造表单头信息
    NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
    [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
    [mutableHeaders setValue:mimeType forKey:@"Content-Type"];

    //创建 AFHTTPBodyPart body的抽象,添加到 bodyStream中
    AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
    bodyPart.stringEncoding = self.stringEncoding;
    bodyPart.headers = mutableHeaders;
    bodyPart.boundary = self.boundary;
    bodyPart.body = fileURL;
    bodyPart.bodyContentLength = [fileAttributes[NSFileSize] unsignedLongLongValue];
    [self.bodyStream appendHTTPBodyPart:bodyPart];

    return YES;
}

- (void)appendPartWithInputStream:(NSInputStream *)inputStream
                             name:(NSString *)name
                         fileName:(NSString *)fileName
                           length:(int64_t)length
                         mimeType:(NSString *)mimeType
{
    NSParameterAssert(name);
    NSParameterAssert(fileName);
    NSParameterAssert(mimeType);

    //构造表单段头信息
    NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
    [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
    [mutableHeaders setValue:mimeType forKey:@"Content-Type"];

    //创建 AFHTTPBodyPart body的抽象,添加到 bodyStream中
    AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
    bodyPart.stringEncoding = self.stringEncoding;
    bodyPart.headers = mutableHeaders;
    bodyPart.boundary = self.boundary;
    bodyPart.body = inputStream;

    bodyPart.bodyContentLength = (unsigned long long)length;

    [self.bodyStream appendHTTPBodyPart:bodyPart];
}


- (NSMutableURLRequest *)requestByFinalizingMultipartFormData {
    if ([self.bodyStream isEmpty]) {
        return self.request;
    }

    // Reset the initial and final boundaries to ensure correct Content-Length
    // 重新设置 初始的和最终的boundaries 确保有正确的 content-length
    [self.bodyStream setInitialAndFinalBoundaries];
    //设置HTTP请求体
    [self.request setHTTPBodyStream:self.bodyStream];

    //重新设置请求头部的 Content-Type
    [self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", self.boundary] forHTTPHeaderField:@"Content-Type"];
    //重新设置请求头部的 Content-Length
    [self.request setValue:[NSString stringWithFormat:@"%llu", [self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"];

    return self.request;
}

@end

AFMultipartBodyStream

@interface  AFMultipartBodyStream : NSInputStream 
//每个段的大小
@property (nonatomic, assign) NSUInteger numberOfBytesInPacket;
//延时时间 在 3G 和 2G 下控制网络拥塞
@property (nonatomic, assign) NSTimeInterval delay;
// ???
@property (nonatomic, strong) NSInputStream *inputStream;
//内容的长度
@property (readonly, nonatomic, assign) unsigned long long contentLength;
//是否为空
@property (readonly, nonatomic, assign, getter = isEmpty) BOOL empty;

//使用编码方案初始化
- (instancetype)initWithStringEncoding:(NSStringEncoding)encoding;
//标记初始和最后的段
- (void)setInitialAndFinalBoundaries;
//添加多表单数据条目
- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart;
@end

主要的方法实现

- (void)setInitialAndFinalBoundaries {
    if ([self.HTTPBodyParts count] > 0) {
        //遍历 AFHTTPBodyPart 数组,设置 bodyPart的 hasInitialBoundary 和 hasFinalBoundary 为NO
        for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
            bodyPart.hasInitialBoundary = NO;
            bodyPart.hasFinalBoundary = NO;
        }
        //标记第一个 boundary
        [[self.HTTPBodyParts firstObject] setHasInitialBoundary:YES];
        //标记最后一个 boundary
        [[self.HTTPBodyParts lastObject] setHasFinalBoundary:YES];
    }
}

- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart {
    //将bodyPart添加到数组中
    [self.HTTPBodyParts addObject:bodyPart];
}

#pragma mark - NSInputStream

- (NSInteger)read:(uint8_t *)buffer
        maxLength:(NSUInteger)length
{
    //inputstream 没有打开则直接关闭
    if ([self streamStatus] == NSStreamStatusClosed) {
        return 0;
    }

    NSInteger totalNumberOfBytesRead = 0;

    while ((NSUInteger)totalNumberOfBytesRead < MIN(length, self.numberOfBytesInPacket)) {
        // currentHTTPBodyPart为空 或者 currentHTTPBodyPart 没有可用字节
        if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart hasBytesAvailable]) {
            
            //下一个AFHTTPBodyPart为空则跳出循环
            if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) {
                break;
            }
        } else {
            //获取还剩下多少字节没有读
            NSUInteger maxLength = MIN(length, self.numberOfBytesInPacket) - (NSUInteger)totalNumberOfBytesRead;
            //调用 AFHTTPBodyPart 的读方法
            NSInteger numberOfBytesRead = [self.currentHTTPBodyPart read:&buffer[totalNumberOfBytesRead] maxLength:maxLength];
            //发生了错误
            if (numberOfBytesRead == -1) {
                self.streamError = self.currentHTTPBodyPart.inputStream.streamError;
                break;
            } else {
                //计算总共读取的字节数
                totalNumberOfBytesRead += numberOfBytesRead;

                //设置了延时时间则延时
                if (self.delay > 0.0f) {
                    [NSThread sleepForTimeInterval:self.delay];
                }
            }
        }
    }

    return totalNumberOfBytesRead;
}
- (BOOL)getBuffer:(__unused uint8_t **)buffer
           length:(__unused NSUInteger *)len
{
    //不支持
    return NO;
}

- (BOOL)hasBytesAvailable {
    //只有在读取时,才能知道是否有剩余字节
    return [self streamStatus] == NSStreamStatusOpen;
}

#pragma mark - NSStream

- (void)open {
    //已经是Open状态直接返回
    if (self.streamStatus == NSStreamStatusOpen) {
        return;
    }
    
    //设置当前状态为打开
    self.streamStatus = NSStreamStatusOpen;
    
    //标记开始和结束的bodyPart
    [self setInitialAndFinalBoundaries];
    
    //初始化迭代器
    self.HTTPBodyPartEnumerator = [self.HTTPBodyParts objectEnumerator];
}

- (void)close {
    //标记当前状态为关闭
    self.streamStatus = NSStreamStatusClosed;
}

//不支持
- (id)propertyForKey:(__unused NSString *)key {
    return nil;
}

//不支持
- (BOOL)setProperty:(__unused id)property
             forKey:(__unused NSString *)key
{
    return NO;
}

//不支持
- (void)scheduleInRunLoop:(__unused NSRunLoop *)aRunLoop
                  forMode:(__unused NSString *)mode
{}

//不支持
- (void)removeFromRunLoop:(__unused NSRunLoop *)aRunLoop
                  forMode:(__unused NSString *)mode
{}

AFHTTPBodyPart

AFHTTPBodyPart 提供了读取多表单条目数据的方法,其中每个条目的body部分使用NSInputStream读取.
**AFHTTPBodyPart 接口 **

@interface AFHTTPBodyPart : NSObject
//编码方案
@property (nonatomic, assign) NSStringEncoding stringEncoding;
//头信息
@property (nonatomic, strong) NSDictionary *headers;
//分割符
@property (nonatomic, copy) NSString *boundary;
//消息体数据
@property (nonatomic, strong) id body;
//消息体数据大小
@property (nonatomic, assign) unsigned long long bodyContentLength;
//输入流
@property (nonatomic, strong) NSInputStream *inputStream;

//是否为起始bodypart,需要对 boundary进行一点处理
@property (nonatomic, assign) BOOL hasInitialBoundary;
//是否为最后一个bodypart, 需要对boundary 进行处理
@property (nonatomic, assign) BOOL hasFinalBoundary;

//是否有可用字节
@property (readonly, nonatomic, assign, getter = hasBytesAvailable) BOOL bytesAvailable;
//总大小
@property (readonly, nonatomic, assign) unsigned long long contentLength;

//读数据放到buffer中
- (NSInteger)read:(uint8_t *)buffer
        maxLength:(NSUInteger)length;
@end

post请求body样本 (multipart/form-data)

 /*
     一个完整的段是结构:
      第一个段 --boundary\r\n ,中间的段\r\n--boundary\r\n
      <--headr info--!> \r\n
      <--headr info--!> \r\n \r\n
      <--body--!>
      如果是最后一个段要加上\r\n--boundary--\r\n
     */
----------------------------798861386718386465039906
Content-Disposition: form-data; name="para0"

123456
----------------------------798861386718386465039906
Content-Disposition: form-data; name="para2"

123456
----------------------------798861386718386465039906--

AFHTTPBodyPart主要方法

- (BOOL)transitionToNextPhase {
    //在主线程执行
    if (![[NSThread currentThread] isMainThread]) {
        dispatch_sync(dispatch_get_main_queue(), ^{
            [self transitionToNextPhase];
        });
        return YES;
    }

    switch (_phase) {
            // AFEncapsulationBoundaryPhase -> AFHeaderPhase
        case AFEncapsulationBoundaryPhase:
            _phase = AFHeaderPhase;
            break;
            // AFHeaderPhase -> AFHeaderPhase
        case AFHeaderPhase:
            //设置input stream 在 runloop中调度
            [self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
             //设置input stream 打开
            [self.inputStream open];
            _phase = AFBodyPhase;
            break;
            //AFBodyPhase -> AFFinalBoundaryPhase
        case AFBodyPhase:
            //关闭流
            [self.inputStream close];
            _phase = AFFinalBoundaryPhase;
            break;
        case AFFinalBoundaryPhase:
        default:
            _phase = AFEncapsulationBoundaryPhase;
            break;
    }
    //重置段读取索引
    _phaseReadOffset = 0;

    return YES;
}

- (NSInteger)read:(uint8_t *)buffer
        maxLength:(NSUInteger)length
{
    NSInteger totalNumberOfBytesRead = 0;
    
    //读取 boundary到buffer中
    if (_phase == AFEncapsulationBoundaryPhase) {
        NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding];
        totalNumberOfBytesRead += [self readData:encapsulationBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
    }
    
    //读取头信息数据到 buffer中
    if (_phase == AFHeaderPhase) {
        NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding];
        totalNumberOfBytesRead += [self readData:headersData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
    }

    //读取body数据到 buffer中
    if (_phase == AFBodyPhase) {
        NSInteger numberOfBytesRead = 0;
       
        //使用input stream 读取  body数据到buffer中
        numberOfBytesRead = [self.inputStream read:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
        if (numberOfBytesRead == -1) {
            return -1;
        } else {
            totalNumberOfBytesRead += numberOfBytesRead;
             //body数据读取完成进入下一个状态
            if ([self.inputStream streamStatus] >= NSStreamStatusAtEnd) {
                [self transitionToNextPhase];
            }
        }
    }
    //读取最后一个段的boundary,到buffer中
    if (_phase == AFFinalBoundaryPhase) {
        NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]);
        totalNumberOfBytesRead += [self readData:closingBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
    }

    return totalNumberOfBytesRead;
}

- (NSInteger)readData:(NSData *)data
           intoBuffer:(uint8_t *)buffer
            maxLength:(NSUInteger)length
{
    //data中 根据 _phaseReadOffset 读取一段数据
    NSRange range = NSMakeRange((NSUInteger)_phaseReadOffset, MIN([data length] - ((NSUInteger)_phaseReadOffset), length));
    [data getBytes:buffer range:range];
    
    //更新偏移
    _phaseReadOffset += range.length;
    
    //如果data已经被读取完毕,跳转到下一个状态
    if (((NSUInteger)_phaseReadOffset) >= [data length]) {
        [self transitionToNextPhase];
    }

    return (NSInteger)range.length;
}

对于POST请求,AFHTTPRequestSerializer内部支持application/x-www-form-urlencodedmultipart/form-data两种序列化方式. 在application/x-www-form-urlencoded模式下,请求体中会存放url encode之后的文本数据. 而在multipart/form-data模式下.请求体中是多段的数据.

你可能感兴趣的:(AFNetworking之HTTP请求的序列化)