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

时间:2013-09-30 14:17:42

标签: objective-c afnetworking-2

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

这是一个例子:

[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?
}];

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

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);

}];

但这看起来很难看。而且还无法看出响应的身体。

有什么建议吗?

7 个答案:

答案 0 :(得分:64)

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

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

以下是在Failure块中获取状态代码的代码:

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

答案 1 :(得分:13)

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

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

文件:JSONResponseSerializerWithData.h:

#import "AFURLResponseSerialization.h"

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

@interface JSONResponseSerializerWithData : AFJSONResponseSerializer
@end

文件: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)在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);
}
  

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

答案 2 :(得分:12)

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

 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;}

答案 3 :(得分:3)

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

AFNetworking正在调用你的失败块,没有任何响应对象,因为它认为发生了真正的失败(例如,可能是HTTP 404响应)。它将404解释为错误的原因是因为404不属于&#34;可接受的状态码&#34;由响应序列化器拥有(可接受代码的默认范围是200-299)。如果您将404(或400或500或其他)添加到该集合,那么具有该代码的响应将被视为可接受,并将被路由到您的成功块 - 完成解码的响应对象

但404是错误的!我希望我的故障块被调用错误!如果是这种情况,则使用接受答案所引用的解决方案:https://github.com/AFNetworking/AFNetworking/issues/1397。但是考虑一下,如果您要提取和处理内容,那么404可能真的很成功。在这种情况下,您的故障块处理真正的故障 - 例如不可解析的域,网络超时等。您可以轻松地检索成功块中的状态代码并进行相应处理。

现在我明白了 - 如果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 );


     }];

答案 4 :(得分:3)

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

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

答案 5 :(得分:2)

在Swift 2.0中(如果你还不能使用Alamofire):

获取状态代码:

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

获取回复数据:

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

某些JSON REST API在其错误响应中返回错误消息(例如,亚马逊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
    }
}

答案 6 :(得分:0)

您可以获取与userInfo对象关联的NSError字典,然后遍历该字典以获得所需的确切响应。例如在我的情况下,我从服务器收到错误,我可以看到ScreeShot中的userInfo

ScreenShot displaying AFNetworking error