使用 AFNetworking 的多部分 PUT 请求
multipart PUT request using AFNetworking
在 iOS 上使用 AFNetworking 编写多部分 PUT 请求的正确方法是什么? (还是Objective-C,不是Swift)
我看起来好像 AFNetworking 可以做多部分 POST
但不能 PUT
,有什么解决方案?
谢谢
您可以使用 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
请求。
您可以创建一个使用 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];
我想出了一个可以处理任何支持方法的解决方案。这是 PUT 的解决方案,但您也可以将其替换为 POST。这是我在基本模型 class.
上调用的类别中的一个方法
- (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
这是适用于我的 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()
对于 RAW Body :
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];
.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 :)
在 iOS 上使用 AFNetworking 编写多部分 PUT 请求的正确方法是什么? (还是Objective-C,不是Swift)
我看起来好像 AFNetworking 可以做多部分 POST
但不能 PUT
,有什么解决方案?
谢谢
您可以使用 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
请求。
您可以创建一个使用 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];
我想出了一个可以处理任何支持方法的解决方案。这是 PUT 的解决方案,但您也可以将其替换为 POST。这是我在基本模型 class.
上调用的类别中的一个方法 - (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
这是适用于我的 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()
对于 RAW Body :
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];
.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 :)