2012-10-15 11 views
35

मैं एक का उपयोग मामला है कि नहीं बल्कि आम होना चाहिए, लेकिन मैं एक आसान तरीका AFNetworking के साथ संभाल करने के लिए नहीं मिल सकता है:AFNetworking: त्रुटि विश्व स्तर पर संभाल कर रखें और अनुरोध को दोहराने

जब भी सर्वर के लिए एक विशिष्ट स्थिति कोड लौटाने

  • एक कैश्ड प्रमाणीकरण टोकन
  • पुन: प्रमाणीकृत (जो एक अलग अनुरोध है)
  • दोहराने असफल अनुरोध को दूर: किसी भी अनुरोध, मैं करना चाहता हूँ।

मैंने सोचा कि यह AFHTTPClient में कुछ वैश्विक समापन/त्रुटि हैंडलर के माध्यम से किया जा सकता है, लेकिन मुझे कुछ भी उपयोगी नहीं मिला। तो, मैं जो चाहता हूं उसे करने का "सही" तरीका क्या है? enqueueHTTPRequestOperation: को मेरे AFHTTPClient सबक्लास में ओवरराइड करें, ऑपरेशन की प्रतिलिपि बनाएँ और मूल समापन हैंडलर को उस ब्लॉक के साथ लपेटें जो मैं चाहता हूं (पुनः प्रमाणित करें, कॉपी किए गए ऑपरेशन को एनक्यू करें)? या मैं गलत रास्ते पर पूरी तरह से हूँ?

धन्यवाद!

संपादित करें: 401 स्थिति कोड को संदर्भित किया गया है, क्योंकि यह शायद टोकन ऑथ का उपयोग करते समय HTTP मूलभूत के लिए आरक्षित है।

उत्तर

20

AFHTTP क्लाइंट की 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; 
+0

मैंने अधिसूचनाओं के बारे में सोचा नहीं है - अनुरोधों को लागू करने के अनुकूलन से कहीं बेहतर और कम घुसपैठ। आपका बहुत बहुत धन्यवाद! प्रमाणीकरण चुनौती ब्लॉक के रूप में: मैं वास्तव में बुनियादी लेख के बजाय टोकन प्रमाणीकरण का उपयोग कर रहा हूं, इसलिए मुझे लगता है कि काम नहीं करेगा, है ना? 401 का उल्लेख करके आपको गुमराह करने के लिए खेद है। बोनस प्रश्न: "अवैध टोकन" के लिए सही प्रतिक्रिया कोड क्या होगा? 400? –

+0

मुझे यकीन नहीं है कि "अवैध टोकन" के लिए सही प्रतिक्रिया कोड क्या है। शायद 403 अधिक उपयुक्त है। – Felix

+1

AFAIK 403 प्रमाणीकरण के बजाय असफल * प्रमाणीकरण * के लिए अधिक है ("प्रमाणीकरण सफल हुआ (यदि कोई है), लेकिन आपको ऐसा करने की अनुमति नहीं है")। लेकिन कभी नहीं, यह एक और सवाल है। आपकी सहायता के लिए एक बार फिर से धन्यवाद। –

27

मैं एक वैकल्पिक AFNetworking 2.0 के साथ ऐसा करने के लिए इसका मतलब का उपयोग ।

आप dataTaskWithRequest:success:failure: उपclass कर सकते हैं और कुछ त्रुटि जांच के साथ पारित समापन ब्लॉक को लपेट सकते हैं। उदाहरण के लिए, यदि आप ओएथ के साथ काम कर रहे हैं, तो आप 401 त्रुटि (समाप्ति) के लिए देख सकते हैं और अपना एक्सेस टोकन रीफ्रेश कर सकते हैं।

- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)urlRequest completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))originalCompletionHandler{ 

    //create a completion block that wraps the original 
    void (^authFailBlock)(NSURLResponse *response, id responseObject, NSError *error) = ^(NSURLResponse *response, id responseObject, NSError *error) 
    { 
     NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; 
     if([httpResponse statusCode] == 401){ 
      NSLog(@"401 auth error!"); 
      //since there was an error, call you refresh method and then redo the original task 
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ 

       //call your method for refreshing OAuth tokens. This is an example: 
       [self refreshAccessToken:^(id responseObject) { 

        NSLog(@"response was %@", responseObject); 
        //store your new token 

        //now, queue up and execute the original task    
        NSURLSessionDataTask *originalTask = [super dataTaskWithRequest:urlRequest completionHandler:originalCompletionHandler]; 
        [originalTask resume]; 
       }];      
      }); 
     }else{ 
      NSLog(@"no auth error"); 
      originalCompletionHandler(response, responseObject, error); 
     } 
    }; 

    NSURLSessionDataTask *task = [super dataTaskWithRequest:urlRequest completionHandler:authFailBlock]; 

    return task; 

} 
+0

अद्भुत कार्यान्वयन और दृष्टिकोण। धन्यवाद। –

2

एक समान दृष्टिकोण लिया, लेकिन इतना मैं कार्रवाई का एक अलग योजना की जरूरत मैं phix23 के जवाब के साथ स्थिति कोड वस्तु नहीं मिल सका। AFNetworking 2.0 ने कुछ चीजें बदल दीं।

-(void)networkRequestDidFinish: (NSNotification *) notification 
{ 
    NSError *error = [notification.userInfo objectForKey:AFNetworkingTaskDidCompleteErrorKey]; 
    NSHTTPURLResponse *httpResponse = error.userInfo[AFNetworkingOperationFailingURLResponseErrorKey]; 
    if (httpResponse.statusCode == 401){ 
     NSLog(@"Error was 401"); 
    } 
} 
+0

क्या आप इसे ऐप डिलीगेट में डाल सकते हैं या क्या इसे उस व्यू कंट्रोलर में जाने की आवश्यकता है जिसे आपने अनुरोध कहा था? –

+1

@TriannBrannon, आपने इसे चयनित उत्तर के पूरक के लिए उपयोग किया। @selector (HTTPOperationDidFinish :) का उपयोग करने के बजाय आप @selector (networkRequestDidFinish :) का उपयोग करते हैं और AFNetworkingOperationDidFinishNotification के बजाय आप AFNetworkingTaskDidCompleteNotification का उपयोग करते हैं। इस तरह मैंने इसे –

1

आप 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) { 

     } 
    } 
}]; 

संपादित करें: वास्तव में यदि आपको प्रतिक्रिया ऑब्जेक्ट में लौटाई गई त्रुटि को संभालने की आवश्यकता है तो उपरोक्त विधि नौकरी नहीं करेगी। अपने AFHTTPSessionManager उपवर्ग में

@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 उपवर्गीकरण रहे हैं उपवर्ग के लिए और एक कस्टम प्रतिक्रिया serializer सेट के साथ यह responseObjectForResponse:data:error: ऐसे ही अतिभारित है हो सकता है

@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 
2

यहाँ है की स्विफ्ट कार्यान्वयन उपयोगकर्ता @adamup के answer

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

जहां रीफ्रेश टोकन (...) एक एक्सटेंशन विधि है जिसे मैंने सर्वर से नया टोकन प्राप्त करने के लिए लिखा था।

+0

पर काम किया है क्या आप जानते हैं कि यह अलामोफायर के साथ कैसे पूरा किया जाता है? – horseshoe7

0

यह सुनिश्चित करने के लिए कि एक ही समय में एकाधिक टोकन रीफ्रेश जारी नहीं किए जाते हैं, तो आपके नेटवर्क अनुरोधों को कतारबद्ध करना और टोकन रीफ्रेश होने पर कतार को अवरुद्ध करना फायदेमंद है, या आपके लिए एक म्यूटेक्स लॉक (@ सिंक्रनाइज़ निर्देश) जोड़ें टोकन ताज़ा विधि।

+0

क्या आप AFNetworking के साथ टोकन चक्र कार्यान्वयन के उपयोग में सहायता कर सकते हैं। हम प्रत्येक अनुरोध पर प्राधिकरण शीर्षलेख नहीं जोड़ना चाहते हैं। टोकन को रीफ्रेश करने के लिए एक साफ तरीका होना चाहिए (401 त्रुटि प्राप्त करने पर) और अनुरोध फिर से करें। –

संबंधित मुद्दे