AFNetworking 2.0 AFHTTPSessionManager:如何在失败块中获取状态代码和响应 JSON?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19096556/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 21:03:13  来源:igfitidea点击:

AFNetworking 2.0 AFHTTPSessionManager: how to get status code and response JSON in failure block?

objective-cafnetworking-2

提问by Oleksandr

When switched to AFNetworking 2.0 the AFHTTPClient has been replaced by AFHTTPRequestOperationManager / AFHTTPSessionManager (as mentioned in the migration guide). The very first issue I came across when using the AFHTTPSessionManager is how to retrieve the body of the response in the failure block?

当切换到 AFNetworking 2.0 时,AFHTTPClient 已被 AFHTTPRequestOperationManager / AFHTTPSessionManager 取代(如迁移指南中所述)。我在使用 AFHTTPSessionManager 时遇到的第一个问题是如何检索失败块中的响应正文?

Here's an example:

下面是一个例子:

[self.sessionManager POST:[endpoint absoluteString] parameters:params success:^(NSURLSessionDataTask *task, id responseObject) {
    // How to get the status code?
} failure:^(NSURLSessionDataTask *task, NSError *error) {
    // How to get the status code? response?
}];

In the success block I would like to retrieve response's status code. In the failure block I would like to retrieve both response's status code and the content (which is JSON in this case that describes the server-side error).

在成功块中,我想检索响应的状态代码。在失败块中,我想检索响应的状态代码和内容(在这种情况下是 JSON,它描述了服务器端错误)。

The NSURLSessionDataTask has a response property of type NSURLResponse, which has not statusCode field. Currently I'm able to retrieve statusCode like this:

NSURLSessionDataTask 有一个 NSURLResponse 类型的响应属性,它没有 statusCode 字段。目前我可以像这样检索 statusCode:

[self.sessionManager POST:[endpoint absoluteString] parameters:params success:^(NSURLSessionDataTask *task, id responseObject) {
    // How to get the status code?
} failure:^(NSURLSessionDataTask *task, NSError *error) {
    NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
    DDLogError(@"Response statusCode: %i", response.statusCode);

}];

But this looks ugly to me. And still can't figure about the response's body.

但这对我来说看起来很丑陋。仍然无法弄清楚响应的主体。

Any suggestions?

有什么建议?

回答by python

You can access the “data” object directly from AFNetworking by using the “AFNetworkingOperationFailingURLResponseDataErrorKey” key so there is no need for subclassing the AFJSONResponseSerializer. You can the serialize the data into a readable dictionary. Here is some sample code to get JSON Data :

您可以使用“AFNetworkingOperationFailingURLResponseDataErrorKey”键直接从 AFNetworking 访问“数据”对象,因此无需对 AFJSONResponseSerializer 进行子类化。您可以将数据序列化为可读字典。以下是获取 JSON 数据的一些示例代码:

 NSData *errorData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey];
 NSDictionary *serializedData = [NSJSONSerialization JSONObjectWithData: errorData options:kNilOptions error:nil];

Here is the code to Get Status code in the Failure block:

以下是获取故障块中的状态代码的代码:

  NSHTTPURLResponse* r = (NSHTTPURLResponse*)task.response;
  NSLog( @"success: %d", r.statusCode ); 

回答by shontauro

After of read and research for several days, It worked for me:

经过几天的阅读和研究,它对我有用:

1) You have to build your own subclass of AFJSONResponseSerializer

1) 您必须构建自己的 AFJSONResponseSerializer 子类

File : JSONResponseSerializerWithData.h:

文件:JSONResponseSerializerWithData.h:

#import "AFURLResponseSerialization.h"

/// NSError userInfo key that will contain response data
static NSString * const JSONResponseSerializerWithDataKey = @"JSONResponseSerializerWithDataKey";

@interface JSONResponseSerializerWithData : AFJSONResponseSerializer
@end

File: JSONResponseSerializerWithData.m

文件:JSONResponseSerializerWithData.m

#import "JSONResponseSerializerWithData.h"

@implementation JSONResponseSerializerWithData

- (id)responseObjectForResponse:(NSURLResponse *)response
                           data:(NSData *)data
                          error:(NSError *__autoreleasing *)error
{
    id JSONObject = [super responseObjectForResponse:response data:data error:error];
    if (*error != nil) {
        NSMutableDictionary *userInfo = [(*error).userInfo mutableCopy];
        if (data == nil) {
//          // NOTE: You might want to convert data to a string here too, up to you.
//          userInfo[JSONResponseSerializerWithDataKey] = @"";
            userInfo[JSONResponseSerializerWithDataKey] = [NSData data];
        } else {
//          // NOTE: You might want to convert data to a string here too, up to you.
//          userInfo[JSONResponseSerializerWithDataKey] = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            userInfo[JSONResponseSerializerWithDataKey] = data;
        }
        NSError *newError = [NSError errorWithDomain:(*error).domain code:(*error).code userInfo:userInfo];
        (*error) = newError;
    }

    return (JSONObject);
}

2) Setup your own JSONResponseSerializer in your AFHTTPSessionManager

2) 在你的 AFHTTPSessionManager 中设置你自己的 JSONResponseSerializer

+ (instancetype)sharedManager
{
    static CustomSharedManager *manager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[CustomSharedManager alloc] initWithBaseURL:<# your base URL #>];

        // *** Use our custom response serializer ***
        manager.responseSerializer = [JSONResponseSerializerWithData serializer];
    });

    return (manager);
}

Source: http://blog.gregfiumara.com/archives/239

来源:http: //blog.gregfiumara.com/archives/239

回答by omarojo

You can get the status code like this, read the failure block...

您可以像这样获取状态代码,读取故障块...

 NSURLSessionDataTask *op = [[IAClient sharedClient] POST:path parameters:paramsDict constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
    } success:^(NSURLSessionDataTask *task, id responseObject) {
        DLog(@"\n============= Entity Saved Success ===\n%@",responseObject);

        completionBlock(responseObject, nil);
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
        DLog(@"\n============== ERROR ====\n%@",error.userInfo);
        NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
        int statuscode = response.statusCode;}

回答by TomSwift

There is another approach besides the accepted answer.

除了接受的答案之外,还有另一种方法。

AFNetworking is calling your failure block, sans any response object, because it believes a true failure has occurred (e.g. a HTTP 404 response, perhaps). The reason it interprets 404 as an error is because 404 isn't in the set of "acceptable status codes" owned by the response serializer (the default range of acceptable codes is 200-299). If you add 404 (or 400, or 500, or whatever) to that set then a response with that code will be deemed acceptable and will be routed to your success block instead - complete with the decoded response object.

AFNetworking 正在调用您的故障块,没有任何响应对象,因为它认为发生了真正的故障(例如,可能是 HTTP 404 响应)。它将 404 解释为错误的原因是因为 404 不在响应序列化程序拥有的“可接受状态代码”集中(可接受代码的默认范围是 200-299)。如果您将 404(或 400,或 500,或其他)添加到该集合,则带有该代码的响应将被视为可接受,并将被路由到您的成功块 -完成解码的响应对象

But 404 is an error! I want my failure block to be called for errors! If that's the case then use the solution referred to by the accepted answer: https://github.com/AFNetworking/AFNetworking/issues/1397. But consider that perhaps a 404 is really a success if you're going to be extracting and processing the content. In this case your failure block handles real failures - e.g. unresolvable domains, network timeouts, etc. You can easily retrieve the status code in your success block and process accordingly.

但是 404 是一个错误!我希望我的失败块因错误而被调用!如果是这种情况,请使用接受的答案中提到的解决方案:https: //github.com/AFNetworking/AFNetworking/issues/1397。但是考虑一下,如果您要提取和处理内容,404 可能真的是成功的。在这种情况下,您的失败块会处理真正的失败——例如无法解析的域、网络超时等。您可以轻松地检索成功块中的状态代码并进行相应的处理。

Now I understand - it might be super nice if AFNetworking passed any responseObject to the failure block. But it doesn't.

现在我明白了 - 如果 AFNetworking 将任何 responseObject 传递给失败块,那可能会非常好。但事实并非如此。

    _sm = [[AFHTTPSessionManager alloc] initWithBaseURL: [NSURL URLWithString: @"http://www.stackoverflow.com" ]];

    _sm.responseSerializer = [AFHTTPResponseSerializer new];
    _sm.responseSerializer.acceptableContentTypes = nil;

    NSMutableIndexSet* codes = [NSMutableIndexSet indexSetWithIndexesInRange: NSMakeRange(200, 100)];
    [codes addIndex: 404];


    _sm.responseSerializer.acceptableStatusCodes = codes;

    [_sm GET: @"doesnt_exist"
  parameters: nil success:^(NSURLSessionDataTask *task, id responseObject) {

      NSHTTPURLResponse* r = (NSHTTPURLResponse*)task.response;

      NSLog( @"success: %d", r.statusCode );

      NSString* s = [[NSString alloc] initWithData: responseObject encoding:NSUTF8StringEncoding];

      NSLog( @"%@", s );

  }
     failure:^(NSURLSessionDataTask *task, NSError *error) {

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


     }];

回答by python

You can access the “data” object directly from AFNetworking by using the “AFNetworkingOperationFailingURLResponseDataErrorKey” key so there is no need for subclassing the AFJSONResponseSerializer. You can the serialize the data into a readable dictionary. Here is some sample code :

您可以使用“AFNetworkingOperationFailingURLResponseDataErrorKey”键直接从 AFNetworking 访问“数据”对象,因此无需对 AFJSONResponseSerializer 进行子类化。您可以将数据序列化为可读字典。这是一些示例代码:

 NSData *errorData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey];
 NSDictionary *serializedData = [NSJSONSerialization JSONObjectWithData: errorData options:kNilOptions error:nil];

回答by seb

In Swift 2.0 (in case you cannot use Alamofire yet):

在 Swift 2.0 中(以防你还不能使用 Alamofire):

Get status code:

获取状态码:

if let response = error.userInfo[AFNetworkingOperationFailingURLResponseErrorKey] as? NSHTTPURLResponse {
    print(response.statusCode)
}

Get response data:

获取响应数据:

if let data = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] as? NSData {
    print("\(data.length)")
}

Some JSON REST APIs return error messages in their error responses (Amazon AWS services for example). I use this function to extract the error message from an NSError that has been thrown by AFNetworking:

某些 JSON REST API 在其错误响应中返回错误消息(例如 Amazon AWS 服务)。我使用此函数从 AFNetworking 抛出的 NSError 中提取错误消息:

// Example: Returns string "error123" for JSON { message: "error123" }
func responseMessageFromError(error: NSError) -> String? {
    do {
        guard let data = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] as? NSData else {
            return nil
        }
        guard let json = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments) as? [String: String] else {
            return nil
        }
        if let message = json["message"] {
            return message
        }
        return nil
    } catch {
        return nil
    }
}

回答by unspokenblabber

You can get the userInfodictionary associated with the NSErrorobject and traverse down that to get the exact response you need. For example in my case I get an error from a server and I can see the userInfolike in ScreeShot

您可以获取userInfoNSError对象关联的字典并向下遍历以获得所需的确切响应。例如,在我的情况下,我从服务器收到错误,我可以userInfo在 ScreeShot 中看到类似的内容

ScreenShot displaying AFNetworking error

屏幕截图显示 AFNetworking 错误