首先說明下form表單請求,是一種特殊的post請求,請求方式有application/x-www-form-urlencoded、multipart/form-data、application/json。其中application/json就是我們常見的post請求,使用AFN直接請求即可。
下面開始正式的內(nèi)容,如何使用AFNetworking完成form表單請求
1、application/x-www-form-urlencoded
直接上代碼:
+ (AFHTTPSessionManager *)getManagerWithReqType:(requestType)reqType{
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
if(reqType == REQ_Form){
[manager.requestSerializer setValue:@"application/x-www-form-urlencoded;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
}else{
manager.requestSerializer = [AFJSONRequestSerializer serializer];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
[manager.requestSerializer setValue:@"application/json;text/html;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
}
return manager;
}
這種form表單請求方式和我們常見的post請求方式,只有這一個地方需要區(qū)別,特別注意以下兩句不能出現(xiàn)在application/x-www-form-urlencoded請求方式中
manager.requestSerializer = [AFJSONRequestSerializer serializer];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
剩下的部分和常見的post請求一致,請求時只需要調(diào)用:
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
2、multipart/form-data
目前個人在項目中遇到的情況,主要用于上傳二進制文件,使用時只需要在常見post請求的基礎(chǔ)上,設(shè)置Content-Type為:
[manager.requestSerializer setValue:@"multipart/form-data; boundary=----WebKitFormBoundaryHzyefUottpz7ltKf"forHTTPHeaderField:@"Content-Type"];
在調(diào)用方法上稍有不同,multipart/form-data調(diào)用AFN的constructingBodyWithBlock方法:
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
以上傳視頻為例:
+(void)uploadVideoUseAFN:(NSURL *)filePath andUrl:(NSString *)urlString{
AFHTTPSessionManager *manager = [self getManager];
/// 要上傳的二進制文件
NSData *videoData = [NSData dataWithContentsOfURL:filePath];
/// 二進制文件在服務(wù)器保存的路勁
NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
formatter.dateFormat =@"yyyyMMddHHmmss";
NSString *str = [formatter stringFromDate:[NSDate date]];
NSString *fileName = [NSString stringWithFormat:@"%@.mp4", str];
NSURLSessionDataTask *task = [manager POST:urlString parameters:nil headers:nil constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
/// 拼接formdata
[formData appendPartWithFileData:videoData
name:@"myFile"
fileName:fileName
mimeType:@"video/mpeg"];
} progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
[task resume];
}
疑點1、關(guān)于mimeType的設(shè)置
使用AFNetworking不需要設(shè)置額外的參數(shù)(如Content-Disposition),因為在AFNetworking完成form提交時,下面的方法會自動設(shè)置Content-Disposition為@"form-data; name=\"%@\"; filename=\"%@\"
- (void)appendPartWithFileData:(NSData *)data
name:(NSString *)name
fileName:(NSString *)fileName
mimeType:(NSString *)mimeType;
覺得不妥的也可以查看appendPartWithFileData的具體實現(xiàn),會發(fā)現(xiàn)如下內(nèi)容:
[mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
[mutableHeaders setValue:mimeType forKey:@"Content-Type"];
這里有一個痛點,就是關(guān)于mimeType的設(shè)置,圖片和視頻都很方便,可以直接設(shè)置為image/jpg、image/png、video/mpeg等(最好和后臺對一下,因為有些開發(fā)會指定文件類型,設(shè)置不一致會請求失敗)。
但對于文件上傳,mimeType設(shè)置什么就比較懵,這里可以設(shè)置萬能類型application/octet-stream,服務(wù)器會自動解析文件類型。關(guān)于如何獲取文件的mimeType,可以查看
疑點2、關(guān)于使用AFNetworking提交Form表單,請求成功了,但是后臺接收到的數(shù)據(jù)為空
正常情況下我們使用form提交,都會在header中設(shè)置:
[manager.requestSerializer setValue:@"multipart/form-data; boundary=----WebKitFormBoundaryHzyefUottpz7ltKf"forHTTPHeaderField:@"Content-Type"];
后臺沒有接收到傳過去的數(shù)據(jù)就是因為設(shè)置了multipart/form-data和boundary。這里最坑的就是設(shè)置了multipart/form-data和boundary,還能請求成功,讓人一度人為自己寫的代碼沒有半點毛病。
解決辦法:
+ (void)uploadImageWIthimageData:(NSData *)imageData{
NSString *requestUrl = @"";
/// 設(shè)置filename
NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
formatter.dateFormat =@"yyyyMMddHHmmss";
NSString *str = [formatter stringFromDate:[NSDate date]];
NSString *fileName = [NSString stringWithFormat:@"%@.jpg", str];
/// 請求配置
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
/// 注意:如果返回結(jié)果是json類型按如下設(shè)置,data類型manager.responseSerializer = [AFHTTPResponseSerializer serializer];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json",@"text/html",@"image/jpeg",@"image/png",@"application/octet-stream",@"text/json",nil];
/// 設(shè)置上傳參數(shù),經(jīng)過測試這個可以不用傳
NSDictionary *params = @{@"file":imageData};
/// 設(shè)置header
NSDictionary *headers = @{@"Authorization":@""};
NSURLSessionDataTask *task = [manager POST:requestUrl parameters:params headers:headers constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
[formData appendPartWithFileData:imageData
name:@"file"
fileName:fileName
mimeType:@"image/jpg"];
} progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
[task resume];
}
你也不要覺得這樣就不是一個multipart/form-data類型的請求了,因為AFNetworking會自動補全這一部分