1. 程式人生 > >AFNetworking 2.0獲取響應頭資訊

AFNetworking 2.0獲取響應頭資訊


前文有提到在初始化的時候可以設定Http的頭資訊,這沒有任何問題,但是在筆者使用過程中,時常是要獲取Http返回的一些頭資訊,在初次用AFNetworking2.0新特性NSURLSessionDataTask的時候,為了獲取返回的頭資訊,搞了兩個晚上,先是度娘,谷歌,StackOverflow,然後各種那個群找人,嘴壺問同事找大神,最後都說沒有用過。就在想要放棄,想跟服務端溝通,不要把必要資訊放在頭資訊中得時候,忽然靈感忽現:

再此講述一下解決過程和最後的解決方法。

用過AFNetworking2.0 中NSURLSessionDataTask的朋友都知道,大概一個請求方式會類似於下面的方法(由於程式碼是從程式中直接拷出來的,所有直接使用會報錯,請見諒)

- (NSURLSessionDataTask *)requestUpdateScheduleWithParam:(NSArray *)param
                                                 success:(void (^)(NSString *))success
                                                 failure:(void (^)(NSString *))failure{
    
    NSError *parseError = nil;
    //NSDictionary轉換為Data
    NSData* jsonData = [NSJSONSerialization dataWithJSONObject:param options:NSJSONWritingPrettyPrinted error:&parseError];
    //Data轉換為JSON
    NSString* str = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    NSDictionary *dic = @{@"param": str};
    NSURLSessionDataTask *task = [self POST:UpdateScheduleRequest parameters:dic success:^(NSURLSessionDataTask *task, id responseObject) {
        
        NSInteger result = [responseObject[@"result"] integerValue];
        if ( result == 0 ) {
            success(@"true");
        } else {
            NSString *message = responseObject[@"message"];
            failure(message);
        }
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
        DLog(@"%@",error.localizedDescription);
        failure(error.localizedDescription);
    }];
    return task;
}
在返回的結果中我們能夠取到的返回資料只有三種:

1、NSURLSessionDataTask *task  返回的和此次請求有關的描述

2、id responseObject                      AFNetworking幫助自動格式化後的結果,與上文初始化HttpClient時預設的responseSerializer對應

3、NSError *error                            AFNetworking內部報錯,網路請求不通的或者資料解析錯誤等

 上面所描述的三種資料中 responseObject和error顯然是不可行的,所以只有在task中尋找希望,所以詳細檢視類NSURLSessionDataTask

發現其中只是簡單繼承於 NSURLSessionTask,於是繼續進入NSURLSessionTask觀察:

會看到其中有個屬性:

@property (readonly, copy) NSURLResponse *response;	    /* may be nil if no response has been received */
這個似乎很接近我們需要的結果,因為往往response中都會包含我們所需要的頭資訊。然後繼續往下檢視NSURLResponse的資訊。

結果在其中只有寥寥數條屬性,關鍵的是沒有我們想要的Header資訊(程式碼篇幅略長,所以刪除了一些註釋)!

@interface NSURLResponse : NSObject <NSSecureCoding, NSCopying>
{
    @package
    NSURLResponseInternal *_internal;
}

/*!
    @method initWithURL:MIMEType:expectedContentLength:textEncodingName:
    @abstract Initialize an NSURLResponse with the provided values.
    @param URL the URL
    @param MIMETYPE the MIME content type of the response
    @param expectedContentLength the expected content length of the associated data
    @param textEncodingName the name of the text encoding for the associated data, if applicable, else nil
    @result The initialized NSURLResponse.
    @discussion This is the designated initializer for NSURLResponse.
*/
- (instancetype)initWithURL:(NSURL *)URL MIMEType:(NSString *)MIMEType expectedContentLength:(NSInteger)length textEncodingName:(NSString *)name;


@property (readonly, copy) NSURL *URL;
@property (readonly, copy) NSString *MIMEType;
@property (readonly) long long expectedContentLength;
@property (readonly, copy) NSString *textEncodingName;
@property (readonly, copy) NSString *suggestedFilename;

@end

這可如何是好!!!希望似乎就此破滅,彷彿看到服務端的大哥大姐投來的鄙視的眼神於吐槽(#¥%#¥……%……¥#@¥#@%¥#……此處省略數萬字)

但是!在NSURLResponse中繼續往下看,可以看到NSURLResponse的一個子類NSHTTPURLResponse,在這個子類中竟然有看著很像的的一個屬性。

/*! 
    @method allHeaderFields
    @abstract Returns a dictionary containing all the HTTP header fields
    of the receiver.
    @discussion By examining this header dictionary, clients can see
    the "raw" header information which was reported to the protocol
    implementation by the HTTP server. This may be of use to
    sophisticated or special-purpose HTTP clients.
    @result A dictionary containing all the HTTP header fields of the
    receiver.
*/
@property (readonly, copy) NSDictionary *allHeaderFields;

恩,對,這個屬性看起來基友可能(因為在使用AFHTTPRequestOperation請求資料的時候,其中也有個NSHTTPURLResponse,同樣的也是在allHeaderFields中取到頭資訊)

但是在NSURLSessionTask中只有NSURLResponse *response而沒有NSHTTPURLResponse *response!父類又不能使用子類中得屬性。再次開始糾結!

結果抱著試試看的心理將NSURLResponse *response強制轉換成NSHTTPURLResponse,結果TMD竟然可以!

- (NSURLSessionDataTask *)requestLoginWithUsername:(NSString *)username
                                          password:(NSString *)password
                                           success:(void (^)(NSString *,LoginDetailsDataModel *))success
                                           failure:(void (^)(NSString *))failure{

    NSDictionary *dic = @{@"username": username, @"password":[self md5:password]};
    NSURLSessionDataTask *task = [self GET:LoginRequest parameters:dic success:^(NSURLSessionDataTask *task, id responseObject) {
        LoginDataModel *loginSuccessDataModel = [[LoginDataModel alloc] initWithDictionary:responseObject];
        NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;  <span style="color:#FF0000;">// 此處強制轉換型別。</span>
        NSInteger result = [loginSuccessDataModel.result integerValue];
        if ( result == 0 ) {
            
            success(response.allHeaderFields[@"sid"],loginSuccessDataModel.userInfo);
        } else {
            NSString *message = loginSuccessDataModel.message;
            DLog(@"%@",message);
            failure(message);
        }
        
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
        DLog(@"%@",error.localizedDescription);
        failure(error.localizedDescription);
    }];
    
    return task;
}

最後問題解決。

這似乎是NSURLSessionDataTask本身的一個問題,希望以後可以得到解決。