一、请求的数据格式
在网络数据传输过程中,服务器是不支持ios对象的,ios端是不能直接提交字符串、字典等,需要在提交前把数据转换为二进制数据。服务器在接收数据后需要对二进制数据进行解析,但服务器有必要知道这些数据格式是json,还是图片,还是音视频呢?这就需要客户端在请求头Content-Type中告知服务器。
在开发中一般请求的数据格式要么是表单,要么是json 。
- 1.1 、x-www-form-urlencoded(表单)
我们发送一个post请求时,默认数据就是以表单的形式提交的。
POST
header:
Content-Type: "x-www-form-urlencoded"
body:
key1=value1&key2=value2
/*key1=value1
key2=value2
*/
NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://apis.juhe.cn/catering/querybycity"]];
request.HTTPMethod=@"POST";
//设置请求数据类型
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
NSData*requestdata=[@"key=9e0169318d2bdaa82abf75f3c0e7d173&city=上海"dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPBody=requestdata;
NSURLSession *session=[NSURLSession sharedSession];
NSURLSessionTask *sessiontask=[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
}];
[sessiontask resume];
1.2 、x-www-form-urlencoded(表单)
header:
Content-Type: "x-www-form-urlencoded"
body:
key1=value1
key2={"key3":value3, "key4": value4}
注意这里还是一个表单,表单里面嵌套了一个JSON字符串
NSURL *url =[NSURL URLWithString:@"http://49.4.5.233:9111/api/getService"];
NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:url];
request.HTTPMethod=@"POST";
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
//转换为jsonstring
NSDictionary *dic=@{
@"nmae":@"zhangsan",
@"jobnumber":@"001"
};
NSData *jsondata=[NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:nil];
NSString *jsonstring=[[NSString alloc]initWithData:jsondata encoding:NSUTF8StringEncoding];
//josnstring拼接到表单里
NSString *paramas=[NSString stringWithFormat:@"key=9e0169318d2bdaa82abf75f3c0e7d173&message=%@",jsonstring];
NSData*requestdata=[paramas dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPBody=requestdata;
NSURLSession *session=[NSURLSession sharedSession];
NSURLSessionTask *sessiontask=[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
}];
[sessiontask resume];
- 2、json
header:
Content-Type: "application/json"
body:
{
"key1": value1,
"key2": value2
}
NSURL *url =[NSURL URLWithString:@"http://nweeeeeikeji.com/jinyunfuni/product/homeVideoRecommend"];
NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:url];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];//设置请求数据类型
NSDictionary *dic=@{
@"imageType":@"homebanner",
@"loginO":@"1"
};
NSData *jsondata=[NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:nil];
request.HTTPMethod=@"POST";
request.HTTPBody=jsondata;
NSURLSession *session =[NSURLSession sharedSession];
NSURLSessionDataTask *sessiontask=[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
}];
[sessiontask resume];
}
二、AFN设置请求数据格式
AFN内部实现
@implementation AFJSONRequestSerializer
[mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]
@implementation AFHTTPRequestSerializer
[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]
通过以上可以知道,AFN设置请求数据格式可以方便的通过AFHTTPSessionManager的requestSerializer属性进行设置。
- 1、x-www-form-urlencoded(表单)
AFHTTPSessionManager *sessionmanger=[[AFHTTPSessionManager alloc]init];
sessionmanger.requestSerializer=[AFHTTPRequestSerializer serializer];
- 2、json
AFHTTPSessionManager *sessionmanger=[[AFHTTPSessionManager alloc]init];
sessionmanger.requestSerializer=[AFJSONRequestSerializer serializer]
三、AFN使用报错探究
源码:
- (void)viewDidLoad {
[super viewDidLoad];
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager POST:@"https://www.baidu.com" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"%@",responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"请求失败--%@",error);
}];
}
运行之后:请求失败--Error Domain=com.alamofire.error.serialization.response Code=-1016 "Request failed: unacceptable content-type: text/html" 。
分析:报错原因就是响应不接受 text/html类型.也就是afn序列化时不支持 text/html类型,我们需要手动设置一下manager.responseSerializer.acceptableContentTypes=[NSSet setWithObjects:@"text/html"].
修改后:
- (void)viewDidLoad {
[super viewDidLoad];
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
//对于服务器返回数据,告诉afn在序列化时支持text/html类型
manager.responseSerializer.acceptableContentTypes=[NSSet setWithObjects:@"text/html",@"application/json", nil];
[manager POST:@"https://www.baidu.com" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"%@",responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"请求失败--%@",error);
}];
}
运行之后: 请求失败--Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}
分析:对于服务器返回的数据,afn默认会把它当作json来处理。很显然,我们请求的百度首页 返回的并不是一个json文本,而是一个html网页,但是AFNetworking并不知道,它坚信请求的结果就是一个json文本!然后固执地以json的形式去解析,显然没办法把一个网页解析成一个字典或者数组,所以产生了上述错误.因此,我们需要手动设置 manager.responseSerializer=[AFHTTPResponseSerializer serializer]告诉afn以二进制的形式对它进行解析就好了。
纠正后:
- (void)viewDidLoad {
[super viewDidLoad];
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
//对于服务器返回数据,告诉afn在序列化时支持text/html类型
manager.responseSerializer.acceptableContentTypes=[NSSet setWithObjects:@"text/html",@"application/json", nil];
//对于服务器返回数据,以二进制的形式进行解析(如果返回的数据既不是xml也不是json那么应该修改成这样)
manager.responseSerializer=[AFHTTPResponseSerializer serializer];
[manager POST:@"https://www.baidu.com" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"%@",responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"请求失败--%@",error);
}];
}
四、图片上传
multipart/form-data 用于文件上传领域,在iOS开发中很多时候需要会涉及到上传图片等服务器交互的操作,基本上全部都会使用Multipart/form-data的请求方式来完成上传.需要我们去严格按照规范的格式来组装请求体 , 每一个换行每一个空格都是不可忽略的 ,AFN已经很好的为我们封装了。
AFHTTPSessionManager *manger = [AFHTTPSessionManager manager];
[manger POST:@"http://120.25.226.186:32812/upload" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
NSData *data=[NSData dataWithContentsOfFile:@"/Users/reitaketachibana/Desktop/屏幕快照 2018-03-27 下午3.38.04.png"];
[formData appendPartWithFileData:data name:@"file1" fileName:@"屏幕快照 2018-03-27 下午3.38.04.png"
mimeType:@"image/png"];
} progress:^(NSProgress * _Nonnull uploadProgress) {
NSLog(@"上传进度");
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"上传成功%@",task.currentRequest.allHTTPHeaderFields);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"上传失败");
}];
网友评论