38

かなり一般的な使用例がありますが、AFNetworking で簡単に処理する方法が見つかりません。

サーバーがリクエストに対して特定のステータス コードを返すたびに、次のことを行います。

  • キャッシュされた認証トークンを削除する
  • 再認証 (これは別の要求です)
  • 失敗した要求を繰り返します。

これは のグローバルな完了/エラー ハンドラを介して実行できると考えましたがAFHTTPClient、役立つものは何も見つかりませんでした。それで、私がやりたいことをする「正しい」方法は何ですか?サブクラスでオーバーライドenqueueHTTPRequestOperation:し、AFHTTPClient操作をコピーして、元の完了ハンドラーを、必要なことを行うブロックでラップします (再認証、コピーされた操作をキューに入れます)? それとも、私は完全に間違った方向に進んでいますか?

ありがとう!

編集: 401 ステータス コードへの参照を削除しました。これは、トークン認証を使用している間、おそらく HTTP Basic 用に予約されているためです。

4

6 に答える 6

21

AFHTTPClientのinitメソッドレジスタでAFNetworkingOperationDidFinishNotification、リクエストの終了後に投稿されます。

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(HTTPOperationDidFinish:) name:AFNetworkingOperationDidFinishNotification object:nil];

通知ハンドラでステータスコードを確認するcopyAFHTTPRequestOperation、新しいコードを作成します。

- (void)HTTPOperationDidFinish:(NSNotification *)notification {
  AFHTTPRequestOperation *operation = (AFHTTPRequestOperation *)[notification object];

    if (![operation isKindOfClass:[AFHTTPRequestOperation class]]) {
        return;
    }

    if ([operation.response statusCode] == 401) {
        // enqueue a new request operation here
    }
}

編集:

一般に、これを行う必要はなく、次のAFNetworkingメソッドで認証を処理するだけです。

- (void)setAuthenticationChallengeBlock:(void (^)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge))block;
于 2012-10-15T15:20:28.170 に答える
4

これは、ユーザー @adamup の回答のSwift実装です。

class SessionManager:AFHTTPSessionManager{
static let sharedInstance = SessionManager()
override func dataTaskWithRequest(request: NSURLRequest!, completionHandler: ((NSURLResponse!, AnyObject!, NSError!) -> Void)!) -> NSURLSessionDataTask! {

    var authFailBlock : (response:NSURLResponse!, responseObject:AnyObject!, error:NSError!) -> Void = {(response:NSURLResponse!, responseObject:AnyObject!, error:NSError!) -> Void in

        var httpResponse = response as! NSHTTPURLResponse

        if httpResponse.statusCode == 401 {
            //println("auth failed")

            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), { () -> Void in

                self.refreshToken(){ token -> Void in
                    if let tkn = token{
                        var mutableRequest = request.mutableCopy() as! NSMutableURLRequest
                        mutableRequest.setValue(tkn, forHTTPHeaderField: "Authorization")
                        var newRequest = mutableRequest.copy() as! NSURLRequest
                        var originalTask = super.dataTaskWithRequest(newRequest, completionHandler: completionHandler)
                        originalTask.resume()
                    }else{
                        completionHandler(response,responseObject,error)
                    }

                }
            })
        }
        else{
            //println("no auth error")
            completionHandler(response,responseObject,error)
        }
    }
    var task = super.dataTaskWithRequest(request, completionHandler:authFailBlock )

    return task
}}

ここで、refreshToken (...) は、サーバーから新しいトークンを取得するために作成した拡張メソッドです。

于 2015-08-12T19:00:46.573 に答える
0

をサブクラス化AFHTTPSessionManagerまたは直接使用しているAFURLSessionManager場合は、次のメソッドを使用して、タスクの完了後に実行されるブロックを設定できます。

/**
 Sets a block to be executed as the last message related to a specific task, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didCompleteWithError:`.

 @param block A block object to be executed when a session task is completed. The block has no return value, and takes three arguments: the session, the task, and any error that occurred in the process of executing the task.
*/
- (void)setTaskDidCompleteBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, NSError *error))block;

その中のセッションの各タスクに対してやりたいことを何でも実行してください:

[self setTaskDidCompleteBlock:^(NSURLSession *session, NSURLSessionTask *task, NSError *error) {
    if ([task.response isKindOfClass:[NSHTTPURLResponse class]]) {
        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
        if (httpResponse.statusCode == 500) {

        }
     }
}];

編集: 実際、応答オブジェクトで返されたエラーを処理する必要がある場合、上記のメソッドは機能しません。サブクラス化する場合の 1 つの方法は、サブクラス化して、そのようにオーバーロードAFHTTPSessionManagerされたカスタム応答シリアライザーを設定することです。responseObjectForResponse:data:error:

@interface MyJSONResponseSerializer : AFJSONResponseSerializer
@end

@implementation MyJSONResponseSerializer

#pragma mark - AFURLResponseSerialization
- (id)responseObjectForResponse:(NSURLResponse *)response
                           data:(NSData *)data
                          error:(NSError *__autoreleasing *)error
{
    id responseObject = [super responseObjectForResponse:response data:data error:error];

    if ([responseObject isKindOfClass:[NSDictionary class]]
        && /* .. check for status or error fields .. */)
    {
        // Handle error globally here
    }

    return responseObject;
}

@end

AFHTTPSessionManagerサブクラスに設定します:

@interface MyAPIClient : AFHTTPSessionManager
+ (instancetype)sharedClient;
@end

@implementation MyAPIClient

+ (instancetype)sharedClient {
    static MyAPIClient *_sharedClient = nil;
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{
        _sharedClient = [[MyAPIClient alloc] initWithBaseURL:[NSURL URLWithString:MyAPIBaseURLString]];
        _sharedClient.responseSerializer = [MyJSONResponseSerializer serializer];
    });

    return _sharedClient;
}

@end
于 2015-06-04T21:27:31.687 に答える
0

複数のトークン更新がほぼ同時に発行されないようにするには、ネットワーク要求をキューに入れ、トークンが更新されているときにキューをブロックするか、ミューテックス ロック (@synchronized ディレクティブ) をトークン更新メソッドに追加することをお勧めします。

于 2016-01-13T21:33:43.490 に答える