AFNetworking可是iOS网络开发的神器,大大简便了操作.不过网络可是重中之重,不能只会用AFNetworking.我觉得网络开发首先要懂基本的理论,例如tcp/ip,http协议,之后要了解web的请求和响应,会使用苹果自带的NSURLSession,最后是把AFNetworking的源码啃掉.
前言
一直以来网络开发用的都是前面同事基于AFNetworking二次封装好的框架,一直都没什么问题,也就没往深处去了解.然后公司开始新项目了,iOS端由我负责,这可是我的第一次啊,从零开始,构建整个项目.这是个挑战,内心还是有点小激动的.
轮子肯定是不用重复造的,网络框架就拿的老项目的,结果出现了两个问题.
- 上传多张图片,服务端解析不了
- 无文件上传, Content-Type还是multipart/form-data
为了解决这个问题,从就没用过的NSURLSession到http协议,追本溯源,终于解决了.
http协议
关于http协议的理论就不多讲了,主要就讲使用POST方法传递数据时,发送的请求头和请求体.
Content-Type
我们提交的数据是什么编码方式服务端是不知道的,其实我们完全可以自定义格式,只要服务端取到数据后能解析就可以了.
一般服务端语言如 php、python 等,以及它们的 framework,都内置了自动解析常见数据格式的功能。所以我们一般都用那几种常见的数据格式,数据格式就是请求头里的Content-Type,服务端根据Content-Type来解析请求体里的数据.
一般有四种最常见的方式:
- application/x-www-form-urlencoded
这是默认的方式,以key1=val1&key2=val2的方式进行编码.
country=0&pro=1023&city=102301
multipart/form-data
这个首先生成了一个 boundary 用于分割不同的字段,为了避免与正文内容重复, boundary 很长很复杂,一般有文件上传的时候用这种方法
--Boundary+A675D0398A56493A
Content-Disposition: form-data; name="photoFiles"; filename="photoFiles.jpeg"
Content-Type: image/jpeg
- application/json
这个非常常见了,感觉它适合格式支持比键值对复杂得多的结构化数据.
{"country":0,"pro":1023,"city":102301}
text/xml
它是一种使用 HTTP 作为传输协议,XML 作为编码方式的远程调用规范.
移动端一般不会用它,太臃肿了,加的标签完全是浪费.
所以传文件用multipart/form-data.
application/json和application/x-www-form-urlencoded差不多,但是application/json数据更加直观,更适合结构更加复杂的数据.
我特地抓包了网易新闻等app,没有文件的情况下是application/json
NSMutableURLRequest设置
既然知道了请求头,请求体.那么给服务器端发送的请求按照上面的格式设置即可.
//上传一张图片为例
NSMutableURLRequest *request= [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:5.0f];
request.HTTPMethod=@"POST";
//数据体设置
NSMutableData *dataM=[NSMutableData data];
NSString *strTop=[NSString stringWithFormat:@"--%@\nContent-Disposition: form-data; name=\"file1\"; filename=\"%@\"\nContent-Type: %@\n\n",kBOUNDARY_STRING,fileName,@"image/jpg"];
NSString *strBottom=[NSString stringWithFormat:@"\n--%@--",kBOUNDARY_STRING];
NSString *filePath=[[NSBundle mainBundle] pathForResource:fileName ofType:nil];
NSData *fileData=[NSData dataWithContentsOfFile:filePath];
[dataM appendData:[strTop dataUsingEncoding:NSUTF8StringEncoding]];
[dataM appendData:fileData];
[dataM appendData:[strBottom dataUsingEncoding:NSUTF8StringEncoding]];
//通过请求头设置
[request setValue:[NSString stringWithFormat:@"%lu",(unsigned long)dataM.length] forHTTPHeaderField:@"Content-Length"];
[request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",kBOUNDARY_STRING] forHTTPHeaderField:@"Content-Type"];
//设置数据体
request.HTTPBody=dataM;
AFNetworking
AFNetworking就是基于NSMutableURLRequest,NSURLSession的封装,非常好用.
以POST为例,它有两个方法:
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))f