接手的项目是要将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数据被认为不完整的原因。
+(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
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];