obj-c AFNetworking 2.0 POST 请求不起作用

Posted

技术标签:

【中文标题】obj-c AFNetworking 2.0 POST 请求不起作用【英文标题】:obj-c AFNetworking 2.0 POST request does not work 【发布时间】:2013-11-22 11:35:08 【问题描述】:

您好想通过使用 AFNetworking 2.0 将一些数据(字符串和文件)发送到服务器。不知何故,POST请求的数据(对于forumlar)不正确,看起来请求上的编码/序列化丢失了。由于服务器无法处理我上传的数据。

如何设置请求的编码/序列化?

我假设必须设置 URL 表单参数编码。文档状态

[[AFHTTPRequestSerializer serializer] requestWithMethod:@"POST" URLString:URLString parameters:parameters];

我试图这样做,但我不知道如何正确地做到这一点。使用以下 Xcode 通过警告:

manager.requestSerializer = [[AFHTTPRequestSerializer serializer] requestWithMethod:@"POST" URLString:URLString parameters:parameters]; 

/.../CameraViewController.m:105:31: 不兼容的指针类型从 'NSMutableURLRequest *' 分配给 'AFHTTPRequestSerializer *'

在我的源代码下面:

CameraViewController.h

#import <UIKit/UIKit.h>

@interface CameraViewController : UIViewController <UIImagePickerControllerDelegate, UINavigationControllerDelegate>
@property (weak, nonatomic) IBOutlet UIImageView *imageView;

@end

CameraViewControllerView.m

#import "CameraViewController.h"
#import "AFHTTPRequestOperationManager.h"

@interface CameraViewController ()    
@property (nonatomic) int photoIsTaken;    
@end

@implementation CameraViewController

// removed unecessary code for this question

- (void)upload 
    NSLog(@"%s: uploader ", __FUNCTION__);
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];

    NSDictionary *parameters = @@"latitude": @"8.444444",
                                 @"longitude": @"50.44444",
                                 @"location": @"New York",
                                 @"type": @"2",
                                 @"claim": @"NYC",
                                 @"flag": @"0",
                                 @"file": UIImageJPEGRepresentation(self.imageView.image,0.2);

NSString *URLString = @"http://192.168.1.157/tapp/laravel/public/foobar/upload";

manager.requestSerializer = [[AFHTTPRequestSerializer serializer] requestWithMethod:@"POST" URLString:URLString parameters:parameters];

[manager POST:URLString parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) 
    NSLog(@"JSON: %@", responseObject);
 failure:^(AFHTTPRequestOperation *operation, NSError *error) 
    NSLog(@"Error: %@, %@", error, operation.responseString);
];

    [self dismissViewControllerAnimated:NO completion:nil];


@end

【问题讨论】:

你真的要发送嵌入在 url 中的整个图像吗? 我想通过 POST 请求发送图像,作为普通公式上传。该解决方案可以在 AFN 1.3 中轻松运行。所以我认为这是要走的路,但 AFN 2.0 的行为不同。会试试你的小费。文件不是总是作为 POST 请求中的参数发送吗? 图像应该进入正文。想一想,这就像您将一个潜在的 url >1MB 粘贴到您的浏览器 URL 栏中! @ErikKerber mulitpart 为我解决了问题。对于文档,我添加了一个 anwser。 【参考方案1】:

终于成功了。很麻烦,但现在我真的很高兴......在我的测试过程中,我在 Wifi 中遇到了一些“请求体流耗尽”的问题,这很奇怪。

下面的代码对我有用。

- (void)upload 

    // !!! only JPG, PNG not covered! Have to cover PNG as well
    NSString *fileName = [NSString stringWithFormat:@"%ld%c%c.jpg", (long)[[NSDate date] timeIntervalSince1970], arc4random_uniform(26) + 'a', arc4random_uniform(26) + 'a'];
    // NSLog(@"FileName == %@", fileName);

    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];

    NSDictionary *parameters = @@"lat": @"8.444444",
                                 @"lng": @"50.44444",
                                 @"location": @"New York",
                                 @"type": @"2",
                                 @"claim": @"NYC",
                                 @"flag": @"0";
     // BASIC AUTH (if you need):
    manager.securityPolicy.allowInvalidCertificates = YES;
    manager.requestSerializer = [AFHTTPRequestSerializer serializer];
    [manager.requestSerializer setAuthorizationHeaderFieldWithUsername:@"foo" password:@"bar"];
    // BASIC AUTH END

    NSString *URLString = @"http://192.168.1.157/tapp/laravel/public/foobar/upload";

    /// !!! only jpg, have to cover png as well
    NSData *imageData = UIImageJPEGRepresentation(self.imageView.image, 0.5); // image size ca. 50 KB
    [manager POST:URLString parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> formData) 
        [formData appendPartWithFileData:imageData name:@"file" fileName:fileName mimeType:@"image/jpeg"];
     success:^(AFHTTPRequestOperation *operation, id responseObject) 
        NSLog(@"Success %@", responseObject);
     failure:^(AFHTTPRequestOperation *operation, NSError *error) 
        NSLog(@"Failure %@, %@", error, operation.responseString);
    ];

    [self dismissViewControllerAnimated:NO completion:nil];

【讨论】:

嗨,如果你不是我的:目前我正在使用 php 服务器,我想知道“ [manager.requestSerializer setAuthorizationHeaderFieldWithUsername:@"foo" password:@"bar"]; " -- > 这是什么意思?我可以从哪里获得这些凭据 如果可以的话,能否请您在这里发布一些适合上述代码的PHP代码 @MannamBrahmam 使用$_POST 变量在您的服务器上获取发布的参数。例如。 var_dump($_POST);【参考方案2】:

感谢@NobleK,类别可能是解决此问题的最佳方法。这是一个示例代码:

@interface AFURLConnectionOperation (AuthenticationChallengeUploadFix)
@end

@implementation AFURLConnectionOperation (AuthenticationChallengeUploadFix)

- (NSInputStream *)connection:(NSURLConnection __unused *)connection needNewBodyStream:(NSURLRequest *)request 
    if ([request.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) 
        return [request.HTTPBodyStream copy];
    
    return nil;


@end

【讨论】:

这是迄今为止该问题的最佳解决方案。令人惊讶的是,这不是 AFNetworking 的一部分。【参考方案3】:

10 多个小时以来,我一直在为这个地狱般的问题寻找好的答案,终于找到了可行的方法!根据 Apple Doc

NSURLErrorRequestBodyStreamExhausted (-1021) 当需要正文流但客户端未提供时返回。这会影响 ios 上使用正文流发送 POST 请求但未实现 NSURLConnection 委托方法 connection:needNewBodyStream 的客户端。

所以我要做的是子类化 AFHTTPRequestOperation 并实现 NSURLConnection 的所有委托方法 //.h

    @interface CGHTTPRequestOperation : AFHTTPRequestOperation

    @end

//.m

    @implementation CGHTTPRequestOperation

    #pragma mark NSURLConnection delegate methods

    - (NSInputStream *)connection:(NSURLConnection __unused *)connection needNewBodyStream:(NSURLRequest *)request 

        if ([request.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) 

            return [request.HTTPBodyStream copy];

        

        return nil;

    

    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response 
        [super connection:connection didReceiveResponse:response];
    

    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
        [super connection:connection didReceiveData:data];
    

    - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite 
        [super connection:connection didSendBodyData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];
    

    - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
        return [super connection:connection willCacheResponse:cachedResponse];
    

    - (void)connectionDidFinishLoading:(NSURLConnection *)connection
        [super connectionDidFinishLoading:connection];
    

    - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 
        [super connection:connection didFailWithError:error];
    

    - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection 
        return YES;
    
    - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge 
        [super connection:connection willSendRequestForAuthenticationChallenge:challenge];
    

    @end

如果您想知道如何使用这个扩展类来上传多部分图像数据,这里是示例 //.h

    typedef enum 

        CGFileUploadStatusError = 0,
        CGFileUploadStatusSuccess = 1,

     CGFileUploadStatus;

    typedef void(^CGNetworkingFileUploadCBlock) (CGFileUploadStatus status,NSString *responseString);

//.m

    + (void) uploadImageAtPath:(NSString *) imagePath cBlock:(CGNetworkingFileUploadCBlock) cBlock 

        AFHTTPRequestSerializer *r = [AFHTTPRequestSerializer serializer];

        NSDictionary *param = @@"param1":@"",@"param2":@"";
        NSData *d = [NSData dataWithContentsOfFile:imagePath];

        __block NSString *paramNameForImage = [imagePath pathComponents].lastObject;

        NSError *error = nil;
        NSMutableURLRequest *urlRequest = [r multipartFormRequestWithMethod:@"POST" URLString:@"http://url_to_up_load_image" parameters:param constructingBodyWithBlock:^(id<AFMultipartFormData> formData) 

                [formData appendPartWithFileData:d name:@"FileUploadControl" fileName:paramNameForImage mimeType:@"image/jpeg"];

             error:&error];

            if (error) 

                NSLog(@"Some error:%@",error);

            



        CGHTTPRequestOperation *requestOperation = [[CGHTTPRequestOperation alloc] initWithRequest:urlRequest];


        //[requestOperation setCredential:nil]; //set cred here
        //[requestOperation setSecurityPolicy:nil]; //set security policy here if you are using one

        [requestOperation setResponseSerializer:[AFHTTPResponseSerializer serializer]];

        [requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) 

            NSLog(@"Success: %@ ***** %@", operation.responseString, operation.response.allHeaderFields);
            cBlock(CGFileUploadStatusSuccess,operation.responseString);

         failure:^(AFHTTPRequestOperation *operation, NSError *error) 

            NSLog(@"Error: %@ ***** %@", operation, error);
            cBlock(CGFileUploadStatusError,operation.responseString);

        ];


        [requestOperation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) 


        ];


        [requestOperation start];
        [requestOperation waitUntilFinished];

    

希望这可以帮助那些遭受此问题的人:)

【讨论】:

感谢您提供此解决方案。它终于解决了我的问题。但是我选择了一种更简单的方法。我没有对AFHTTPRequestOperation 进行子类化,而是在其上创建了一个包含connection:needNewBodyStream 方法的类别。这就是所有需要的。我的其余代码保持不变。

以上是关于obj-c AFNetworking 2.0 POST 请求不起作用的主要内容,如果未能解决你的问题,请参考以下文章

从 AFNetworking 1.3 迁移到 AFNetworking 2.0 的问题

AFNetworking 2.0 JSON 解析

AFNetworking 2.0 对现有项目的更新

AfNetworking 2.0 发布问题

如何在旧的 AFNetworking 中使用 AFNetworking 2.0+?

Afnetworking 2.0 发布方法错误