AFNetworkingでエラー時にレスポンスを参照する。

NO IMAGE

AFNetworkingはデフォルトでは、HTTPレスポンスコードが200系でない場合、failureブロックを起動するのですが、failureにはresponseObjectが渡されないので、400系や500系が返ってくるとクライアントで処理が細かく行えない。

調べたが、意外とはっきりと記述してある情報がなかった。

例えば、クライアントからのリクエストパラメータが不正だった場合、(100文字の箇所にそれ以上送信したなど)failure一括になってしまい、細かくクライアントに結果を見せることができない。

デフォルトでやる方法はわからないが、参考となるページから、オーバライドで実装した。

まず、AFHTTPSessionManagerをオーバライド

#import <Foundation/Foundation.h>
#import <AFNetworking/AFHTTPSessionManager.h>

@interface SenatusNetworkManager : AFHTTPSessionManager

// このメソッドはお好みで
+ (SenatusNetworkManager*)manager;
+ (NSDictionary*)errorResponse:(NSDictionary*)e;
+ (NSInteger)errorCode:(NSDictionary*)e;

実装ファイルは

@implementation SenatusNetworkManager

// 定数
static SenatusNetworkManager* sharedInstance = nil;
static NSString* kErrorResponseObjectKey = @"NetworkManagerErrorResponseKey";
static NSString* kErrorCodeObjectKey = @"NetworkingManagerErrorCodeKey";


// メソッドのオーバーライド
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                            completionHandler:(void (^)(NSURLResponse *, id, NSError *))originalCompletionHandler {
    return [super dataTaskWithRequest:request
                    completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
                        
                        // エラー時にエラーオブジェクトに詰める
                        if (error && responseObject) {
                            NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;

                            if (error.userInfo) { // すでにオブジェクトがある場合(存在理由不明)
                                NSMutableDictionary *userInfo = [error.userInfo mutableCopy];
                                userInfo[kErrorResponseObjectKey] = responseObject;

                                // 参考サイト情報に加え、HTTPステータスコードも保持
                                userInfo[kErrorCodeObjectKey] = [NSNumber numberWithInteger:[httpResponse statusCode]];

                                error = [NSError errorWithDomain:error.domain
                                                            code:error.code
                                                        userInfo:[userInfo copy]];
                            } else { // オブジェクトがない場合、生成
                                error = [NSError errorWithDomain:error.domain
                                                            code:error.code
                                                        userInfo:@{kErrorResponseObjectKey: responseObject, kErrorCodeObjectKey:[NSNumber numberWithInteger:[httpResponse statusCode]]}];
                            }
                        }
                        
                        // オリジナルハンドラーの呼び出し
                        if (originalCompletionHandler) {
                            originalCompletionHandler(response, responseObject, error);
                        }
                    }];
}

// シングルトン(特に不要)
+ (SenatusNetworkManager*)manager
{
    if (!sharedInstance) {
        sharedInstance = [SenatusNetworkManager new];
    }
    return sharedInstance;
}

// エラーのレスポンスを受け取るヘルバーメソッド
+ (NSDictionary*)errorResponse:(NSDictionary*)e {
    return e[kErrorResponseObjectKey];
}

// エラーのステータスコードを受け取るヘルバーメソッド
+ (NSInteger)errorCode:(NSDictionary*)e {
    return [e[kErrorCodeObjectKey] integerValue];
}

参考サイト:Accessing the response body in failure blocks with AFNetworking 2

考えてみると、当たり前なのですが、デフォルトで提供されていると思ってしまった。。。