POST发送form-data的一些总结

接手的项目是要将IOS上的SDK移植到Mac上使用,取到IOS版本的SDK代码阅读后发现仍然在使用ASIHttpReqest作为网络请求的模块,但是众所周知ASIHttpReqest已经停止更新已有4年之久,虽然ASIHttpReqest是一个非常优秀的网络库,但因为缺少社区维护,随着技术的发展,还是有一定的局限。

相比之下AFNetworking是替代ASIHTTPRequest最佳之选。所以决定先将SDK功能业务部分采用AFNetworking进行代码重构[AFNetworking和ASIHTTPRequest的比较]

原先IOS版本的SDK直接使用ASIFormDataRequest通过setPostValue将要发送的数据一个一个添加到postData中,开始采用AFNetworking之后直接使用AFHTTPSessionManager中

- (NSURLSessionDataTask *)POST:(NSString *)URLString
                    parameters:(id)parameters
                       success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                       failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure


发现,POST的数据在服务端被认为是缺少数据,同样是POST操作,为什么会有不同的结果呢?使用Charles抓包分析后,发现使用AFHTTPSessionManager发送的数据是MultiPart,ASIHTTPRequest发送的是x-www-form-urlencoded类型,所以判断服务器只能接收x-www-form-urlencoded类型的数据,也就是&key=value样式的。

继续上网查资料,POST发送数据,但是会有多种数据提交格式,[四种常见的POST提交数据方式],查看了AFNetworking中POST代码后,发现使用到的Content-Type是multipart/form-data。至此找到服务器对我使用AFHTTPSessionManager发送的POST数据被认为不完整的原因。

于是直接使用NSURLConnection写了以下的方法实现POST发送form-data到服务端。


+(void)universalRequestWith:(NSDictionary *)parameters URLStr:(NSString *)URLStr method:(RequestMethodType)methodType andBlock:(void (^)(NSDictionary *response, NSError *error))block{
    NSURL *url=[NSURL URLWithString:URLStr];
    
    
    
    if (methodType == RequestMethodPostType) {
        dispatch_queue_t myCustomQueue = dispatch_queue_create("com.myown.UniversalRequestQueue", NULL);
        dispatch_async(myCustomQueue, ^(){
            NSMutableData *postBody=[NSMutableData data];
            NSUInteger i=0;
            for (NSString *key in [parameters allKeys]) {
                NSString *data = [NSString stringWithFormat:@"%@=%@%@", key, [parameters valueForKey:key],(i<[[parameters allKeys] count]-1 ?  @"&" : @"")];
                [postBody appendData:[data dataUsingEncoding:NSUTF8StringEncoding]];
                i++;
            }
            NSMutableURLRequest *request=[[NSMutableURLRequest alloc]initWithURL:url
                                                                     cachePolicy:NSURLRequestReloadIgnoringCacheData
                                                                 timeoutInterval:20.0f];
            [request setHTTPMethod: @"POST"];
            [request setValue: @"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
            [request setHTTPBody:postBody];
            
            NSError *error = nil;
            NSDictionary *result = [[NSDictionary alloc]init];
            NSHTTPURLResponse* urlResponse = nil;
            
            NSData *responseData = [NSURLConnection sendSynchronousRequest:request
                                                         returningResponse:&urlResponse error:&error];
            if (error.code==0) {
                
                result =[NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:nil];
            }
            
            dispatch_async(dispatch_get_main_queue(), ^(){
                
                if (block) {
                    block(result,error);
                }
            });
        });
    }

}


再对POST提交数据的方式进行仔细研究,发现其中application/x-www-form-urlencoded编码其实是基于uri的percent-encoding编码的,所以采用application/x-www-form-urlencoded的POST数据和queryString只是形式不同,本质都是传递参数。哪是否意味着可以直接使用GET方式去提交参数呢?

于是使用了AFHTTPSessionManager的

- (NSURLSessionDataTask *)GET:(NSString *)URLString
                   parameters:(id)parameters
                      success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                      failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure

最终发现,数据提交成功,返回了正确的结果。

接下来对AFNetworking再仔细研究。

但是又有疑虑,既然AFNetworking应该更全面,为什么会没有支持application/x-www-form-urlencoded的处理方式呢?在源码中搜索application/x-www-form-urlencoded,发现AFURLRequestSerialization的方法

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(id)parameters
                                        error:(NSError *__autoreleasing *)error

中是有使用application/x-www-form-urlencoded作为 Content-Type来封装Http请求的,使用效果怎么样?简单写了一段测试,用AFHTTPSessionManager的 requestSerializer去调用AFNetworking提供的封装方法,也是可以得到正确的结果。

    NSError *error;
    NSHTTPURLResponse* urlResponse = nil;
    
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:[NSURL URLWithString:URLStr]];
   NSURLRequest *AFRequest = [manager.requestSerializer requestBySerializingRequest:[request copy] withParameters:parameters error:&error];
    NSData *responseData = [NSURLConnection sendSynchronousRequest:AFRequest
                                                 returningResponse:&urlResponse error:&error];
    NSDictionary *result =[NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:nil];

综上,解决了form-data的提交,也对POST不同的数据提交方式有了初步的认识和了解,该类问题以后处理起来也将更游刃有余。


你可能感兴趣的:(ios,重构,网络,mac,移植)