iOS開發之配置POST網絡請求

本文主要是講一下我們在請求網絡的時候如何去配置相關的參數然後成功得到響應。那本文以請求百度的API中圖像識別爲例來說一下該如何去配置。

目的需求

我需要把一張包含二維碼的圖片通過百度的圖像識別接口來判定是否包含二維碼。

準備工作

1>裝有Xcode的MAC電腦一臺
2>準備一張二維碼圖片,這個隨便找一張,或者去草料二維碼製作一張也是OK的
3>百度雲登錄註冊,並創建一個圖像識別應用,拿到API Key和Secret Key
4>百度對接文檔:圖像審覈組合接口

開始對接

1>創建工程項目,然後創建一個網絡請求。那麼此處我選擇AFN,並且是手動導入框架。首先這個過程是要先通過API Key和Secret Key獲取AccessToken,然後再通過AccessToken再將圖片通過POST請求發送給百度鑑別,然後返回結果。

Snip20170812_28.png

 

獲取AccessToken的代碼:

 

- (void)viewDidLoad {
    [super viewDidLoad];
     
    [self addBtn];
}

- (void)addBtn{
    UIButton *btn = [[UIButton alloc]init];
    btn.frame = CGRectMake(0, 300, 200, 50);
    CGFloat btnCenterX = self.view.center.x;
    CGFloat btnCenterY = btn.frame.origin.y + btn.frame.size.height * 0.5;
    CGPoint btnCenter = CGPointMake(btnCenterX, btnCenterY);
    btn.center = btnCenter;
    btn.backgroundColor = [UIColor orangeColor];
    [self.view addSubview:btn];
    [btn setTitle:@"點我獲取Token" forState:UIControlStateNormal];
    [btn addTarget:self action:@selector(didClickBtn) forControlEvents:UIControlEventTouchUpInside];
}

- (void)didClickBtn{
    [self getAccessToken];
}

- (void)getAccessToken{
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    //一但用了這個返回的那個responseObject就是NSData,如果不用就是簡單的
    //manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    //manager.requestSerializer = [AFJSONRequestSerializer serializer];
    manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/html",@"image/jpeg",@"text/plain", nil];
    NSDictionary *dict = @{
                           @"grant_type":@"client_credentials",
                           @"client_id":@"填寫你的API Key",
                           @"client_secret":@"填寫你的Secret Key"
                           };
    [manager POST:@"https://aip.baidubce.com/oauth/2.0/token" parameters:dict progress:nil success:
     ^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
         /*
         if ([responseObject isKindOfClass:[NSData class]]) {
             NSError *error;
             id object = [NSJSONSerialization JSONObjectWithData:responseObject options:0 error:&error];
             NSLog(@"%@",object);
         }
         if (![responseObject isKindOfClass:[NSDictionary class]]) {
             NSLog(@"不是想要的結果,應該是哪裏出錯了,請注意檢查!");
             NSLog(@"responseObject = %@",responseObject);
             return;
         }
        */
         NSDictionary *dict = (NSDictionary *)responseObject;
         
         NSString *accessToken = dict[@"access_token"];
         _accessToken = dict[@"access_token"];
         NSLog(@"%@",accessToken);
         
         //調用判定二維碼的接口
         [self getResultOfQRImageWithAFN1];
         //[self getResultOfQRImageWithAFN2];
         //[self getResultOfQRImageWithNSURLSession];
         
     } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
         NSLog(@"請求失敗--%@",error);
     }];
}

PS :注意以下有一點要說一下
下面這句代碼意思是得到的響應會序列化,何爲序列化,序列化就是將其他的數據類型轉成二進制,所以一旦我們加上了這句話,那麼我們在接收響應的時候,就要反序列化,將二進制的NSData轉成其他響應的數據類型

 

manager.responseSerializer = [AFHTTPResponseSerializer serializer];

2>獲取AccessToken之後就需要去請求圖像識別接口了

Snip20170812_26.png

Snip20170812_27.png

Snip20170812_29.png

 

首先請求是POST,我們再說下以下幾個要點:
要點一:
參數中有URL參數,那麼這個URL參數是直接拼接在URL後面的,這個要注意
要點二:
其中說到的Header表示的是我們傳給百度接口的是內容Content-Type是Json格式的,並且是UTF-8編碼格式的,所以這個要特別注意,很多請求失敗可能是這些設置沒有設置好才導致的application/json;charset=utf-8
要點三:
請求體應該怎麼放,在AFN中如果是使用POST的URL方式的接口中的parameters這個參數其實就是請求體,並且這是id類型的,只要我們設置好請求的內容格式然後這個參數傳入對應格式的內容即可

PS: 這裏面就涉及到請求體,那怎麼設置請求體呢在AFN中,有以下的三種方式:

方式一:

 

//這個是用Request方式
- (void)getResultOfQRImageWithAFN1{
    
    //由於是POST,將一些憑證如AccessToken之類的拼接在一起,這種叫URL參數,同GET一樣
    NSString *urlString = @"https://aip.baidubce.com/api/v1/solution/direct/img_censor";
    NSString *newURLString = [NSString stringWithFormat:@"%@?access_token=%@?",urlString,_accessToken];
    NSURL *url = [NSURL URLWithString:newURLString];
    //創建請求request
    NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:30];
    //設置請求方式爲POST
    request.HTTPMethod = @"POST";
    //設置請求內容格式
    [request setValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
    //設置請求體參數
    NSString *base64String = [self base64EncodeImageWithName:@"qrcode"];
    NSArray *array = @[@"watermark"];
    NSDictionary *params = @{@"image":base64String,
                             @"scenes": array};
    //這是設置請求體,把參數放進請求體(這部分的參數也叫請求參數)
    NSString *paramJsonStr = [KODJsonService dictionaryToJson:params];
    NSLog(@"paramString = %@",paramJsonStr);
    NSData *data = [paramJsonStr dataUsingEncoding:NSUTF8StringEncoding];
    NSLog(@"data = %@",data);
    request.HTTPBody = [paramJsonStr dataUsingEncoding:NSUTF8StringEncoding];
    
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/html",@"image/jpeg",@"text/plain", nil];
    [[manager dataTaskWithRequest:request completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
        NSLog(@"請求成功---%@---%@",responseObject,[responseObject class]);
        
        if (error) {
            NSLog(@"%@",error);
            return ;
        }
        NSDictionary *dict = (NSDictionary *)responseObject;
        //以下結構沒做放空處理,只爲了儘快看到效果,請大家自行處理下
        NSDictionary *resultDict = dict[@"result"];
        if (!resultDict) {
            NSLog(@"result沒有值,很抱歉,估計錯誤了!");
            return;
        }
        NSDictionary *newDict = resultDict[@"watermark"];
        NSArray *resultArray = newDict[@"result"];
        for (NSDictionary *dict in resultArray) {
            if ([dict[@"type"] isEqualToString:@"QR code"]) {
                NSLog(@"這絕壁有一張二維碼!!!");
            }
        }
    }] resume];
}

PS: 這種方式是用manager直接通過request的方式去請求而不是設置URL的方式,另外request是可以設置HTTPBody的,這樣更加的清晰

方式二:

 

//這個是用URL方式的
- (void)getResultOfQRImageWithAFN2{

    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    //這句話加了之後返回的responseObject就是JSONData了,如果不加那就是正常的JSON可以直接轉成字典然後操作
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    manager.requestSerializer = [AFJSONRequestSerializer serializer];
    [manager.requestSerializer setValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
    manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/html",@"image/jpeg",@"text/plain", nil];
    NSString *urlString = @"https://aip.baidubce.com/api/v1/solution/direct/img_censor";
    NSString *newURLString = [NSString stringWithFormat:@"%@?access_token=%@?",urlString,_accessToken];
    
    NSString *base64String = [self base64EncodeImageWithName:@"qrcode"];
    NSArray *array = @[@"watermark"];
    NSDictionary *params = @{@"image":base64String,
                             @"scenes": array};
    
    [manager POST:newURLString parameters:params progress:nil success:
     ^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
         NSLog(@"請求成功---%@---%@",responseObject,[responseObject class]);
         NSLog(@"成功!!!");
         NSError *error;

         id object = [NSJSONSerialization JSONObjectWithData:responseObject options:0 error:&error];
         if ([object isKindOfClass:[NSDictionary class]]) {
             NSDictionary *response = (NSDictionary *)object;
             NSLog(@"%@",response);
             //以下結構沒做放空處理
             NSDictionary *resultDict = response[@"result"];
             if (!resultDict) {
                 NSLog(@"result沒有值,很抱歉,估計錯誤了!");
                 return;
             }
             NSDictionary *newDict = resultDict[@"watermark"];
             NSArray *resultArray = newDict[@"result"];
             for (NSDictionary *dict in resultArray) {
                 if ([dict[@"type"] isEqualToString:@"QR code"]) {
                     NSLog(@"這絕壁有一張二維碼!!!");
                 }
             }
         }
     } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
         NSLog(@"請求失敗--%@",error);
     }];
}

PS: 大家要注意一下以下這兩句代碼的作用,第一句是設置請求上傳內容是Json格式的,我試了以下如果單單設置第二句是會返回400Bad Request錯誤的,如果僅僅設置第一句是OK的

 

manager.requestSerializer = [AFJSONRequestSerializer serializer];
    [manager.requestSerializer setValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"];

我看了下AFN的內部實現其實已經設置好了,大家有興趣可以去查看一下AFN的內部源碼

 

+ (instancetype)serializer {
    return [self serializerWithWritingOptions:(NSJSONWritingOptions)0];
}

+ (instancetype)serializerWithWritingOptions:(NSJSONWritingOptions)writingOptions
{
    AFJSONRequestSerializer *serializer = [[self alloc] init];
    serializer.writingOptions = writingOptions;

    return serializer;
}

最終都會調用

 

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(id)parameters
                                        error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(request);

    if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
        return [super requestBySerializingRequest:request withParameters:parameters error:error];
    }

    NSMutableURLRequest *mutableRequest = [request mutableCopy];

    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
        if (![request valueForHTTPHeaderField:field]) {
            [mutableRequest setValue:value forHTTPHeaderField:field];
        }
    }];

    if (parameters) {
        if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
            [mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
        }

        [mutableRequest setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]];
    }

    return mutableRequest;
}

所以看到這裏你會發現AFN封裝了好大一圈,一會兒在文末貼出純系統NSURLSession怎麼實現

另外有兩個AFHTTPRequestSerializer的子類,一個是AFJSONRequestSerializer,這個是會把參數編碼成Json格式,然後設置Content-Type爲application/json,但是並沒有進行UTF-8編碼,所以這個還是要注意一下自己編碼

 

/**
 `AFJSONRequestSerializer` is a subclass of `AFHTTPRequestSerializer` that encodes parameters as JSON using `NSJSONSerialization`, setting the `Content-Type` of the encoded request to `application/json`.
 */
@interface AFJSONRequestSerializer : AFHTTPRequestSerializer

另一個是AFPropertyListRequestSerializer,這個可以猜一下,其實就是XML,並且設置爲application/x-plist

 

/**
 `AFPropertyListRequestSerializer` is a subclass of `AFHTTPRequestSerializer` that encodes parameters as JSON using `NSPropertyListSerializer`, setting the `Content-Type` of the encoded request to `application/x-plist`.
 */
@interface AFPropertyListRequestSerializer : AFHTTPRequestSerializer

方式三:

 

- (void)getResultOfQRImageWithAFN3{
    
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    //這句話加了之後返回的responseObject就是JSONData了,如果不加那就是正常的JSON可以直接轉成字典然後操作
    //manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    manager.requestSerializer = [AFJSONRequestSerializer serializer];
    [manager.requestSerializer setValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
    manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/html",@"image/jpeg",@"text/plain", nil];
    NSString *urlString = @"https://aip.baidubce.com/api/v1/solution/direct/img_censor";
    NSString *newURLString = [NSString stringWithFormat:@"%@?access_token=%@?",urlString,_accessToken];
    
    NSString *base64String = [self base64EncodeImageWithName:@"qrcode"];
    NSArray *array = @[@"watermark"];
    NSDictionary *params = @{@"image":base64String,
                             @"scenes": array};
    NSMutableURLRequest *request = [manager.requestSerializer requestWithMethod:@"POST" URLString:newURLString parameters:params error:nil];
    [[manager dataTaskWithRequest:request completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
        NSLog(@"%@",error);
        NSLog(@"%@",responseObject);
    }] resume];
}

對比原生NSURLSession的做法

 

-(void)getResultOfQRImageWithNSURLSession
{
    NSString *urlString = @"https://aip.baidubce.com/api/v1/solution/direct/img_censor";
    NSString *newURLString = [NSString stringWithFormat:@"%@?access_token=%@?",urlString,_accessToken];
    NSURL *url = [NSURL URLWithString:newURLString];
    NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:30];
    request.HTTPMethod = @"POST";
    [request setValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
    NSString *base64String = [self base64EncodeImageWithName:@"qrcode"];
    NSArray *array = @[@"watermark"];
    NSDictionary *params = @{@"image":base64String,
                             @"scenes": array};
    NSString *paramJsonStr = [KODJsonService dictionaryToJson:params];
    NSLog(@"paramString = %@",paramJsonStr);
    NSData *data = [paramJsonStr dataUsingEncoding:NSUTF8StringEncoding];
    NSLog(@"data = %@",data);
    request.HTTPBody = [paramJsonStr dataUsingEncoding:NSUTF8StringEncoding];
    
    // 3.獲得會話對象
    NSURLSession *session = [NSURLSession sharedSession];
    // 4.根據會話對象,創建一個Task任務
    NSURLSessionDataTask *sessionDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        //判斷statusCode
        NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
        if (!(res.statusCode == 200 || error)) {
            NSLog(@"失敗!!!");
            return;
        }
        NSLog(@"從服務器獲取到數據");
        if (error) {
            NSLog(@"error:%@",error.description);
            return ;
        }
        NSLog(@"%@",data);
        
        NSError *newError;
        id object = [NSJSONSerialization JSONObjectWithData:data options:0 error:&newError];
        if ([object isKindOfClass:[NSDictionary class]]) {
            NSDictionary *response = (NSDictionary *)object;
            NSLog(@"%@",response);
            //這裏記得把這個值取出來,很深,哈哈哈...
        }
    }];
    //5.最後一步,執行任務,(一定調用這句話)
    [sessionDataTask resume];
}

附件

其中用到了一個私有方法:

 

- (NSString *)base64EncodeImageWithName:(NSString *)name{
    UIImage *image = [UIImage imageNamed:name];
    NSData *data = UIImagePNGRepresentation(image);
    NSData *base64Data = [data base64EncodedDataWithOptions:0];
    NSString *baseString = [[NSString alloc]initWithData:base64Data encoding:NSUTF8StringEncoding];
    
    return baseString;
}

 

//這是我的一個工具類裏面的方法,大家可以改成對象方法直接替換調用即可
+ (NSString*)dictionaryToJson:(NSDictionary *)dic
{
    if (dic.allKeys.count == 0){
#ifdef DSDUBUG
        NSLog(@"%@---%s",self.class,__FUNCTION__);
        NSLog(@"您傳入的字典爲空,無法轉換,請確保字典不爲空!!!");
#endif
        return nil;
    }
    NSError *parseError = nil;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:&parseError];
    return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}

圖片:

qrcode.png

文末總結

其實今天就講了一個很重要的問題,設置請求體,並且設置請求的內容格式的問題。另外一個是我們其實可以對系統的方法NSURLSession進行封裝,系統的已經很好了,其實有時間的話我們可以針對於我們的業務自己封裝會更好,因爲AFN封裝了一層又一層太繞了...

AFN中的如果是GET請求,用URL方式的那個接口中的parameters參數其實就是我們需要拼接在URL後面的參數,所以這個通常只需要轉一下UTF-8就好
AFN中的如果是POST請求,用URL方式的那個接口中的parameters參數其實就是我們需要上傳給後臺的參數,因爲要上傳所以需要設置一下內容格式,一般上傳是上傳Json,也有上傳Form表單數據的比如圖片等等;另外這個URL參數就需要跟URL做一下拼接然後整體放置在以下接口中的newURLString:

 

[manager POST:newURLString parameters:params progress:nil success:
     ^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
//code here...
}];

以上!!!



作者:KODIE
鏈接:https://www.jianshu.com/p/8fa6c51ec05e
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章