【问题标题】:multipart PUT request using AFNetworking使用 AFNetworking 的多部分 PUT 请求
【发布时间】:2015-05-22 03:40:32
【问题描述】:

在 iOS 上使用 AFNetworking 对多部分 PUT 请求进行编码的正确方法是什么? (仍然是 Objective-C,不是 Swift)

我看起来好像 AFNetworking 可以做多部分 POST 但不能做 PUT,有什么解决方案?

谢谢

【问题讨论】:

    标签: ios objective-c rest afnetworking afnetworking-2


    【解决方案1】:

    您可以使用 multipartFormRequestWithMethod 创建包含所需数据的多部分 PUT 请求。

    例如,在 AFNetworking v3.x 中:

    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    
    NSError *error;
    NSURLRequest *request = [manager.requestSerializer multipartFormRequestWithMethod:@"PUT" URLString:@"http://example.com/rest/api/" parameters:@{@"foo" : @"bar"} constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
        NSString *value = @"qux";
        NSData *data = [value dataUsingEncoding:NSUTF8StringEncoding];
        [formData appendPartWithFormData:data name:@"baz"];
    } error:&error];
    
    NSURLSessionDataTask *task = [manager dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
        if (error) {
            NSLog(@"%@", error);
            return;
        }
    
        // if you want to know what the statusCode was
    
        if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
            NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
            NSLog(@"statusCode: %ld", statusCode);
        }
    
        NSLog(@"%@", responseObject);
    }];
    [task resume];
    

    如果是AFNetworking 2.x,可以使用AFHTTPRequestOperationManager

    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    
    NSError *error;
    NSURLRequest *request = [manager.requestSerializer multipartFormRequestWithMethod:@"PUT" URLString:@"http://example.com/rest/api/" parameters:@{@"foo" : @"bar"} constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
        NSString *value = @"qux";
        NSData *data = [value dataUsingEncoding:NSUTF8StringEncoding];
        [formData appendPartWithFormData:data name:@"baz"];
    } error:&error];
    
    AFHTTPRequestOperation *operation = [manager HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
        NSLog(@"%@", responseObject);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"%@", error);
    }];
    
    [manager.operationQueue addOperation:operation];
    

    已经说明了如何创建这样的请求,值得注意的是服务器可能无法解析它们。值得注意的是,PHP 解析多部分 POST 请求,但不解析多部分 PUT 请求。

    【讨论】:

    • 尝试了上面的代码,但出现以下错误{“image”:[“提交的数据不是文件。检查表单上的编码类型。” ] }
    • 您使用的是appendPartWithFormData 还是appendPartWithFileData?听起来你想要后者。
    • @Rob 我已经实现了您的解决方案,但我将字节数据作为 responseObject。你能在这里解决这个问题吗:stackoverflow.com/questions/40424323/…
    • @Rob 请问如何从会话任务中获取返回的状态码?
    • @Llg - 它是response 对象的一部分。所以,检查它是否是一个NSHTTPURLResponse 对象,如果是,抓住它的statusCode。请参阅上面的修改示例。
    【解决方案2】:

    这是适用于我的 Afnetworking 3.0 和 Swift 的代码。我知道它的旧线程,但可能对某人很方便!

        let manager: AFHTTPSessionManager = AFHTTPSessionManager()
    
        let URL = "\(baseURL)\(url)"        
    
        let request: NSMutableURLRequest = manager.requestSerializer.multipartFormRequestWithMethod("PUT", URLString: URL, parameters: parameters as? [String : AnyObject], constructingBodyWithBlock: {(formData: AFMultipartFormData!) -> Void in
            formData.appendPartWithFileData(image!, name: "Photo", fileName: "photo.jpg", mimeType: "image/jpeg")
            }, error: nil)
    
        manager.dataTaskWithRequest(request) { (response, responseObject, error) -> Void in
    
            if((error == nil)) {
                print(responseObject)
                completionHandler(responseObject as! NSDictionary,nil)
            }
            else {
                print(error)
                completionHandler(nil,error)
            }
    
            print(responseObject)
            }.resume()
    

    【讨论】:

      【解决方案3】:

      你可以创建一个使用AFHTTPRequestSerialization的多部分表单请求方法构造的NSURLRequest

      NSString *url = [[NSURL URLWithString:path relativeToURL:manager.baseURL] absoluteString];
      id block = ^(id<AFMultipartFormData> formData) {
          [formData appendPartWithFileData:media
                                      name:@"image"
                                  fileName:@"image"
                                  mimeType:@"image/jpeg"];
      };
      
      NSMutableURLRequest *request = [manager.requestSerializer
                                      multipartFormRequestWithMethod:@"PUT"
                                      URLString:url
                                      parameters:nil
                                      constructingBodyWithBlock:block
                                      error:nil];
      
      [manager HTTPRequestOperationWithRequest:request success:successBlock failure:failureBlock];
      

      【讨论】:

        【解决方案4】:

        我想出了一个可以处理任何受支持方法的解决方案。这是 PUT 的解决方案,但您也可以将其替换为 POST。这是我在基础模型类上调用的类别中的一个方法。

            - (void)updateWithArrayOfFiles:(NSArray *)arrayOfFiles forKey:(NSString *)key params:(NSDictionary *)params success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure {
        
                id block = [self multipartFormConstructionBlockWithArayOfFiles:arrayOfFiles forKey:key failureBlock:failure];
        
                NSMutableURLRequest *request = [[self manager].requestSerializer
                                                multipartFormRequestWithMethod:@"PUT"
                                                URLString:self.defaultURL
                                                parameters:nil
                                                constructingBodyWithBlock:block
                                                error:nil];
        
               AFHTTPRequestOperation *operation = [[self manager] HTTPRequestOperationWithRequest:request success:success failure:failure];
               [operation start];
            }
        
            #pragma mark multipartForm constructionBlock
        
            - (void (^)(id <AFMultipartFormData> formData))multipartFormConstructionBlockWithArayOfFiles:(NSArray *)arrayOfFiles forKey:(NSString *)key failureBlock:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure {
                id block = ^(id<AFMultipartFormData> formData) {
                    int i = 0;
                    // form mimeType
                    for (FileWrapper *fileWrapper in arrayOfFiles) {
                        NSString *mimeType = nil;
                        switch (fileWrapper.fileType) {
                            case FileTypePhoto:
                                mimeType = @"image/jpeg";
                                break;
                            case FileTypeVideo:
                                mimeType = @"video/mp4";
                                break;
                            default:
                                break;
                        }
                        // form imageKey
                        NSString *imageName = key;
                        if (arrayOfFiles.count > 1)
                            // add array specificator if more than one file
                            imageName = [imageName stringByAppendingString: [NSString stringWithFormat:@"[%d]",i++]];
                        // specify file name if not presented
                        if (!fileWrapper.fileName)
                            fileWrapper.fileName  = [NSString stringWithFormat:@"image_%d.jpg",i];
                        NSError *error = nil;
        
                        // Make the magic happen
                        [formData appendPartWithFileURL:[NSURL fileURLWithPath:fileWrapper.filePath]
                                                   name:imageName
                                               fileName:fileWrapper.fileName
                                               mimeType:mimeType
                                                  error:&error];
                        if (error) {
                            // Handle Error
                            [ErrorManager logError:error];
                            failure(nil, error);
                        }
                    }
                };
                return block;
            }
        

        因为它使用 FileWrapper 接口

            typedef NS_ENUM(NSInteger, FileType) {
                FileTypePhoto,
                FileTypeVideo,
            };
        
        
        @interface FileWrapper : NSObject
        
        @property (nonatomic, strong) NSString *filePath;
        @property (nonatomic, strong) NSString *fileName;
        @property (assign, nonatomic) FileType fileType;
        
        @end
        

        【讨论】:

          【解决方案5】:

          对于 RAW 正文:

          NSData *data = someData; NSMutableURLRequest *requeust = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[self getURLWith:urlService]]];

          [reqeust setHTTPMethod:@"PUT"];
          [reqeust setHTTPBody:data];
          [reqeust setValue:@"application/raw" forHTTPHeaderField:@"Content-Type"];
          
          NSURLSessionDataTask *task = [manager uploadTaskWithRequest:requeust fromData:nil progress:^(NSProgress * _Nonnull uploadProgress) {
          
          } completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
          
          }];
          [task resume];
          

          【讨论】:

            【解决方案6】:

            .h

            + (void)PUT:(NSString *)URLString
             parameters:(id)parameters
            constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
               progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                success:(void (^)(NSURLResponse *response, id responseObject))success
                failure:(void (^)(NSURLResponse * response, NSError *error))failure
                  error:(NSError *__autoreleasing *)requestError;
            

            .m:

            + (void)PUT:(NSString *)URLString
             parameters:(id)parameters
            constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
            progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                success:(void (^)(NSURLResponse * _Nonnull response, id responseObject))success
                failure:(void (^)(NSURLResponse * _Nonnull response, NSError *error))failure
            error:(NSError *__autoreleasing *)requestError {
            
                NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer]
                                                multipartFormRequestWithMethod:@"PUT"
                                                URLString:(NSString *)URLString
                                                parameters:(NSDictionary *)parameters
                                                constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
                                                error:(NSError *__autoreleasing *)requestError];
                AFURLSessionManager *manager = [AFURLSessionManager sharedManager];//[AFURLSessionManager manager]
                NSURLSessionUploadTask *uploadTask;
                uploadTask = [manager uploadTaskWithStreamedRequest:(NSURLRequest *)request
                                                           progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                                                  completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
                                                      if (error) {
                                                          failure(response, error);
                                                      } else {
                                                          success(response, responseObject);
                                                      }
                                                  }];
            
                [uploadTask resume];
            }
            

            就像经典的 afnetworking。 把它放到你的网络上 Util :)

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-03-16
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多