美文网首页
iOS 使用AFNetworking的form表单请求

iOS 使用AFNetworking的form表单请求

作者: 宥落 | 来源:发表于2021-03-15 13:08 被阅读0次

    首先说明下form表单请求,是一种特殊的post请求,请求方式有application/x-www-form-urlencodedmultipart/form-dataapplication/json。其中application/json就是我们常见的post请求,使用AFN直接请求即可。

    下面开始正式的内容,如何使用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请求方式,只有这一个地方需要区别,特别注意以下两句不能出现在application/x-www-form-urlencoded请求方式中

    manager.requestSerializer = [AFJSONRequestSerializer serializer];
    manager.responseSerializer = [AFJSONResponseSerializer serializer];
    

    剩下的部分和常见的post请求一致,请求时只需要调用:

    - (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请求的基础上,设置Content-Type为:

    [manager.requestSerializer setValue:@"multipart/form-data; boundary=----WebKitFormBoundaryHzyefUottpz7ltKf"forHTTPHeaderField:@"Content-Type"];
    

    在调用方法上稍有不同,multipart/form-data调用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];
    
        /// 二进制文件在服务器保存的路劲
        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、关于mimeType的设置

    使用AFNetworking不需要设置额外的参数(如Content-Disposition),因为在AFNetworking完成form提交时,下面的方法会自动设置Content-Disposition@"form-data; name=\"%@\"; filename=\"%@\"

    - (void)appendPartWithFileData:(NSData *)data
                              name:(NSString *)name
                          fileName:(NSString *)fileName
                          mimeType:(NSString *)mimeType;
    

    觉得不妥的也可以查看appendPartWithFileData的具体实现,会发现如下内容:

    [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
    [mutableHeaders setValue:mimeType forKey:@"Content-Type"];
    

    这里有一个痛点,就是关于mimeType的设置,图片和视频都很方便,可以直接设置为image/jpgimage/pngvideo/mpeg等(最好和后台对一下,因为有些开发会指定文件类型,设置不一致会请求失败)。

    但对于文件上传,mimeType设置什么就比较懵,这里可以设置万能类型application/octet-stream,服务器会自动解析文件类型。关于如何获取文件的mimeType,可以查看

    疑点2、关于使用AFNetworking提交Form表单,请求成功了,但是后台接收到的数据为空

    正常情况下我们使用form提交,都会在header中设置:

    [manager.requestSerializer setValue:@"multipart/form-data; boundary=----WebKitFormBoundaryHzyefUottpz7ltKf"forHTTPHeaderField:@"Content-Type"];
    

    后台没有接收到传过去的数据就是因为设置了multipart/form-databoundary。这里最坑的就是设置了multipart/form-databoundary,还能请求成功,让人一度人为自己写的代码没有半点毛病。

    解决办法:

    + (void)uploadImageWIthimageData:(NSData *)imageData{
        NSString *requestUrl = @"";
        
        /// 设置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];
        /// 注意:如果返回结果是json类型按如下设置,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];
        
        /// 设置上传参数,经过测试这个可以不用传
        NSDictionary *params = @{@"file":imageData};
        
        /// 设置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会自动补全这一部分

    相关文章

      网友评论

          本文标题:iOS 使用AFNetworking的form表单请求

          本文链接:https://www.haomeiwen.com/subject/znlkcltx.html