iOS 使用AFNetworking的form表單請求

首先說明下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-databoundary。這里最坑的就是設(shè)置了multipart/form-databoundary,還能請求成功,讓人一度人為自己寫的代碼沒有半點毛病。

解決辦法:

+ (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會自動補全這一部分

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容