现在我们经常用到请求数据的方式是http+json方式,但是,在某些情况下,后台提供给你的方式是http的表单请求方式,今天就总结一下表单请求方式的实现。
先来看一下表单的一般形式:
在表单中,我们需要注意的有以下几点:
- 1.我们需要自己设置Content-Type和Content-Length
- 2.在表单中,boundary是一个很重要的东西,他可以是一个随机的字符串,它是将参数隔开的标志,但需要注意的是,这个标志在整个表单中必须一致
- 3.我们在设置参数时,需要指定Content-Disposition,其中,name为参数对于的key值。如果参数为文件格式,同时还要指定filename以及Content-Type
- 4.其中最重要的一点,就是在设置参数的value时,必须在值之前加入一个换行。同时,在http中,换行为\r\n
- 5.参数的分隔是以"--"拼接上boundary,然后拼接上"\r\n"来实现的,同时,在所有参数结束后,需要以"--"拼接boundary再拼接"--"来结束。
接下来写一个简短的例子来记录用法,在这个例子中,接口需要传入两个参数,一个为简单的参数,另一个为文件参数。
- 1.我们可以自己拼接出表单形式
//接口地址
NSString *path = kURL;
//boundary
NSString *theBoundary = @"myBoundary";
//访问请求
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:path]];
request.HTTPMethod = @"POST";
//用来拼接参数
NSMutableData *data = [NSMutableData data];
//拼接第一个参数
[data appendData:[[NSString stringWithFormat:@"--%@\r\n", theBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
//拼接参数名
[data appendData:[@"Content-Disposition:form-data;name=\"uid\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
//拼接参数值
[data appendData:[@"11230953" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
//拼接第二个参数
[data appendData:[[NSString stringWithFormat:@"--%@\r\n", theBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
//拼接参数名
[data appendData:[@"Content-Disposition:form-data;name=\"file\";filename=\"myText.txt\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
//拼接文件类型
[data appendData:[@"Content-Type:text/plain" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:[@"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
//拼接参数值
[data appendData:[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"myText" ofType:@"txt"]]];
[data appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
//拼接结束标志
[data appendData:[[NSString stringWithFormat:@"--%@--", theBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
request.HTTPBody = data;
[request setValue:[NSString stringWithFormat:@"multipart/form-data;boundary=%@", theBoundary] forHTTPHeaderField:@"Content-Type"];
[request setValue:[NSString stringWithFormat:@"%ld", data.length] forHTTPHeaderField:@"Content-Length"];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
NSLog(@"%@", dic);
}];
[dataTask resume];
- 2.既然是网络请求,就不能忘掉强大的AFN,AFN提供了表单的请求方法,下图是AFN主页所讲:
我们可以用AFN来实现上述接口的传参:
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:kURL parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFormData:[@"11230953" dataUsingEncoding:NSUTF8StringEncoding] name:@"uid"];
[formData appendPartWithFileData:[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"myText" ofType:@"txt"]] name:@"file" fileName:@"myText.txt" mimeType:@"text/plain"];
} error:nil];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionUploadTask *uploadTask;
uploadTask = [manager
uploadTaskWithStreamedRequest:request
progress:^(NSProgress * _Nonnull uploadProgress) {
}
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
if (error) {
NSLog(@"Error: %@", error);
} else {
NSLog(@"%@ %@", response, responseObject);
}
}];
[uploadTask resume];
注意,有时可能会报以下错误:
这里我们可以去工程中查找AFN的源码,其中有一个文件为:AFURLResponseSerialization.m,我们在第220行左右做一些修改,将所缺少类型加上即可:
修改前:
self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];
修改后:
self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/plain", nil];