51

当切换到 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);

}];

但这在我看来很难看。而且仍然无法弄清楚响应的主体。

有什么建议么?

4

7 回答 7

68

您可以使用“AFNetworkingOperationFailingURLResponseDataErrorKey”键直接从 AFNetworking 访问“数据”对象,因此无需子类化 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 ); 
于 2015-01-13T08:08:08.333 回答
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

于 2014-07-09T04:03:42.277 回答
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;}
于 2014-03-11T22:09:14.817 回答
4

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

 NSData *errorData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey];
 NSDictionary *serializedData = [NSJSONSerialization JSONObjectWithData: errorData options:kNilOptions error:nil];
于 2015-01-13T08:05:26.517 回答
3

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

AFNetworking 正在调用您的失败块,没有任何响应对象,因为它认为发生了真正的失败(例如,可能是 HTTP 404 响应)。它将 404 解释为错误的原因是因为 404 不在响应序列化程序拥有的“可接受状态代码”集中(可接受代码的默认范围是 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 );


     }];
于 2014-05-17T15:17:05.173 回答
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 在其错误响应中返回错误消息(例如 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
    }
}
于 2015-07-28T10:32:54.910 回答
0

You can get the userInfo dictionary associated with the NSError object 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 userInfo like in ScreeShot

ScreenShot displaying AFNetworking error

于 2013-09-30T19:29:03.030 回答