AFNetworking3 請求失敗時,獲取業(yè)務(wù)邏輯報文信息

寫在前面:在接口的設(shè)計上,一般來說都會有兩個 statusCode:一個代表通訊協(xié)議層面,一個代表業(yè)務(wù)層面。但是,在某些特定的場景,這兩者之間可能混淆。也就是說,程序需要在通訊失敗的情況下,獲取服務(wù)器返回給前端的一些報文信息。

  • 為什么會存在這樣的問題

1.正常情況下,交易應(yīng)該是:通訊狀態(tài)碼(200)+業(yè)務(wù)邏輯狀態(tài)碼(自定義)去處理每只交易的。但某個例存在,通訊狀態(tài)碼(非200)+業(yè)務(wù)邏輯狀態(tài)碼(自定義)去處理某些特殊情況。

2.在AF3.0版本中 當(dāng)交易處理完畢時,當(dāng)http通訊協(xié)議級別上返回的狀態(tài)碼是200時,框架會將業(yè)務(wù)邏輯報文返回給上一層;在通訊失敗時,返回的是 錯誤信息,并沒有將我們需要的報文返回給上一層。

  • 該問題適用的場景

報文來說明場景
  • AF2.0 ,獲取服務(wù)器返回的錯誤信息

AFHTTPRequestOperation *operation =[[AFHTTPRequestOperation alloc]initWithRequest:request];
    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
     {
        success(operation, responseObject);
         
     } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
      //通訊協(xié)議狀態(tài)碼
         NSInteger statusCode = operation.response.statusCode;
      //服務(wù)器返回的業(yè)務(wù)邏輯報文信息
         NSDictionary *dict = operation.responseObject;
          failure(operation,error);
     }];

為了簡潔上面的代碼刪除了一些公共方法,在af的失敗回調(diào)方法中,operation的這兩個方法,可以將服務(wù)器返回的信息幫你取到。
那么,為什么2.0會如此輕易完成目標(biāo)呢,我們不妨看一下其post方法是如何實現(xiàn)的

#pragma mark - AFHTTPRequestOperation
-(void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
                              failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
    // completionBlock is manually nilled out in AFURLConnectionOperation to break the retain cycle.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
#pragma clang diagnostic ignored "-Wgnu"
    self.completionBlock = ^{
        if (self.completionGroup) {
            dispatch_group_enter(self.completionGroup);
        }
//在這個位置 我們已經(jīng)找到了 請求完畢時處理方法
 dispatch_async(http_request_operation_processing_queue(), ^{
//當(dāng)通訊失敗的時候,failure返回的是(self,self.error),而這個self指代的當(dāng)前對象是AFHTTPRequestOperation實例,在它的屬性方法中,存在responseObject,因此2.0方法,天然的可以解決此類問題
            if (self.error) {
                if (failure) {
                    dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
                        failure(self, self.error);
                    });
                }
            } else {
                id responseObject = self.responseObject;
                if (self.error) {
                    if (failure) {
                        dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
                            failure(self, self.error);
                        });
                    }
                } else {
                    if (success) {
                        dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
                            success(self, responseObject);
                        });
                    }
                }
            }

            if (self.completionGroup) {
                dispatch_group_leave(self.completionGroup);
            }
        });
    };
#pragma clang diagnostic pop
}

從源碼中,我們可以發(fā)現(xiàn):當(dāng)通訊失敗的時候,failure返回的是(self,self.error),而這個self指代的當(dāng)前對象是AFHTTPRequestOperation實例,在它的屬性方法中,存在responseObject,因此2.0版本,天然的可以解決此類問題

  • AF3.0 ,如何獲取服務(wù)器返回的部分信息

[manager POST:urlStr parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        success(manager,responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
    NSHTTPURLResponse *response = (NSHTTPURLResponse*)task.response;
 //通訊協(xié)議狀態(tài)碼
    NSInteger statusCode = response.statusCode;
 //服務(wù)器返回的業(yè)務(wù)邏輯報文信息
    NSDictionary *json = __dataSource.loginTimeOutDic;
    failure(task,error);
   }];

在3.0版本中,通訊協(xié)議的狀態(tài)碼還是很容易獲取到的。但是,業(yè)務(wù)邏輯報文就沒那么容易拿到了。這個問題,我們可以往下研究一下3.0的實現(xiàn)代碼。
1.3.0版本的post請求方法

-(NSURLSessionDataTask *)POST:(NSString *)URLString
                    parameters:(id)parameters
                      progress:(void (^)(NSProgress * _Nonnull))uploadProgress
                       success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
                       failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
//在這里發(fā)情了請求,我們繼續(xù)往下探尋
    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters uploadProgress:uploadProgress downloadProgress:nil success:success failure:failure];

    [dataTask resume];

    return dataTask;
}
-(NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                       URLString:(NSString *)URLString
                                      parameters:(id)parameters
                                  uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
                                downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
                                         success:(void (^)(NSURLSessionDataTask *, id))success
                                         failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
    NSError *serializationError = nil;
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
    if (serializationError) {
        if (failure) {
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });
        }

        return nil;
    }
//在這個位置 我們找到了 post請求處理完畢時的回調(diào)方法
    __block NSURLSessionDataTask *dataTask = nil;
    dataTask = [self dataTaskWithRequest:request
                          uploadProgress:uploadProgress
                        downloadProgress:downloadProgress
                       completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
        if (error) {
            if (failure) {
//先使用一種low逼的方法 用一個全局變量將邏輯報文信息帶回
                __dataSource.loginTimeOutDic = responseObject;
//原因在這里,3.0版本并沒有將邏輯報文返回到上一頁面
                failure(dataTask, error);
            }
        } else {
            if (success) {
                success(dataTask, responseObject);
            }
        }
    }];

    return dataTask;
}

2.這種修改第三方成熟框架的方法,從個人開發(fā)經(jīng)驗來說,并不推崇。
當(dāng)使用cocopods管理第三方工具的時候,并沒有辦法去修改第三方的框架,當(dāng)更新之后,會出現(xiàn)代碼被覆蓋掉的問題。而且,修改這類代碼可能會出現(xiàn)意想不到的問題,雖然可以通過擴展的方式,不影響原代碼邏輯,但畢竟是不方便。因此,我推薦用下面這種方法去實現(xiàn),這個功能。

[manager POST:urlStr parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        success(manager,responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
    NSHTTPURLResponse *response = (NSHTTPURLResponse*)task.response;
 //通訊協(xié)議狀態(tài)碼
    NSInteger statusCode = response.statusCode;
 //服務(wù)器返回的業(yè)務(wù)邏輯報文信息
  NSString* errResponse = [[NSString alloc] initWithData:(NSData *)error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] encoding:NSUTF8StringEncoding];
 NSDictionary *json = [self dictionaryWithJson_String:errResponse];
  failure(task,error);
   }];

注意,在這里取值的userInfo,是根據(jù)post請求完畢處理的方法,下面發(fā)現(xiàn)的代理方法中,

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容