web-dev-qa-db-ja.com

AFNetworking 2.0 AFHTTPSessionManager:失敗ブロックでステータスコードと応答JSONを取得する方法

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には、statusCodeフィールドを持たないNSURLResponseタイプの応答プロパティがあります。現在、次のように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);

}];

しかし、これは見苦しいです。そして、応答の本文についてはまだ理解できません。

助言がありますか?

51
Oleksandr

「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 ); 
64
python

数日間読んで研究した後、それは私のために働いた:

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

13
shontauro

あなたはこのようなステータスコードを取得し、失敗ブロックを読むことができます...

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

「AFNetworkingOperationFailingURLResponseDataErrorKey」キーを使用してAFNetworkingから「data」オブジェクトに直接アクセスできるため、AFJSONResponseSerializerをサブクラス化する必要はありません。データを読み取り可能な辞書にシリアル化できます。以下にサンプルコードを示します。

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

受け入れられた答え以外に別のアプローチがあります。

AFNetworkingは、真の障害(おそらくHTTP 404応答など)が発生したと考えているため、応答オブジェクトを使用せずに障害ブロックを呼び出しています。 404をエラーとして解釈する理由は、404が応答シリアライザが所有する「許容可能なステータスコード」のセットにないためです(許容可能なコードのデフォルト範囲は200〜299です)。そのセットに404(または400、または500など)を追加すると、そのコードの応答は受け入れ可能と見なされ、代わりに成功ブロックにルーティングされます-complete with decode応答オブジェクト

しかし、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 );


     }];
3
TomSwift

In 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
    }
}
2
seb

userInfoオブジェクトに関連付けられたNSError辞書を取得し、それを走査して、必要な正確な応答を取得できます。たとえば、私の場合、サーバーからエラーを受け取り、ScreeShotのようなuserInfoを見ることができます

ScreenShot displaying AFNetworking error

0
unspokenblabber