寫在前面:在接口的設計上,一般來說都會有兩個 statusCode:一個代表通訊協(xié)議層面敏簿,一個代表業(yè)務層面明也。但是,在某些特定的場景惯裕,這兩者之間可能混淆温数。也就是說,程序需要在通訊失敗的情況下蜻势,獲取服務器返回給前端的一些報文信息撑刺。
-
為什么會存在這樣的問題
1.正常情況下,交易應該是:通訊狀態(tài)碼(200)+業(yè)務邏輯狀態(tài)碼(自定義)去處理每只交易的握玛。但某個例存在够傍,通訊狀態(tài)碼(非200)+業(yè)務邏輯狀態(tài)碼(自定義)去處理某些特殊情況。
2.在AF3.0版本中 當交易處理完畢時挠铲,當http通訊協(xié)議級別上返回的狀態(tài)碼是200時冕屯,框架會將業(yè)務邏輯報文返回給上一層;在通訊失敗時拂苹,返回的是 錯誤信息安聘,并沒有將我們需要的報文返回給上一層。
-
該問題適用的場景
-
AF2.0 醋寝,獲取服務器返回的錯誤信息
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;
//服務器返回的業(yè)務邏輯報文信息
NSDictionary *dict = operation.responseObject;
failure(operation,error);
}];
為了簡潔上面的代碼刪除了一些公共方法搞挣,在af的失敗回調(diào)方法中带迟,operation的這兩個方法音羞,可以將服務器返回的信息幫你取到。
那么仓犬,為什么2.0會如此輕易完成目標呢嗅绰,我們不妨看一下其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(), ^{
//當通訊失敗的時候,failure返回的是(self窘面,self.error)翠语,而這個self指代的當前對象是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):當通訊失敗的時候谍夭,failure返回的是(self,self.error)憨募,而這個self指代的當前對象是AFHTTPRequestOperation實例紧索,在它的屬性方法中,存在responseObject菜谣,因此2.0版本珠漂,天然的可以解決此類問題
-
AF3.0 ,如何獲取服務器返回的部分信息
[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;
//服務器返回的業(yè)務邏輯報文信息
NSDictionary *json = __dataSource.loginTimeOutDic;
failure(task,error);
}];
在3.0版本中尾膊,通訊協(xié)議的狀態(tài)碼還是很容易獲取到的媳危。但是,業(yè)務邏輯報文就沒那么容易拿到了冈敛。這個問題济舆,我們可以往下研究一下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)驗來說齐邦,并不推崇椎侠。
當使用cocopods管理第三方工具的時候,并沒有辦法去修改第三方的框架措拇,當更新之后我纪,會出現(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;
//服務器返回的業(yè)務邏輯報文信息
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)的代理方法中跪腹,