本文主要是講一下我們在請求網(wǎng)絡(luò)的時候如何去配置相關(guān)的參數(shù)然后成功得到響應(yīng)。那本文以請求百度的API中圖像識別為例來說一下該如何去配置。
目的需求
我需要把一張包含二維碼的圖片通過百度的圖像識別接口來判定是否包含二維碼。
準備工作
1>裝有Xcode的MAC電腦一臺
2>準備一張二維碼圖片,這個隨便找一張,或者去草料二維碼制作一張也是OK的
3>百度云登錄注冊,并創(chuàng)建一個圖像識別應(yīng)用,拿到API Key和Secret Key
4>百度對接文檔:圖像審核組合接口
開始對接
1>創(chuàng)建工程項目,然后創(chuàng)建一個網(wǎng)絡(luò)請求。那么此處我選擇AFN,并且是手動導(dǎo)入框架。首先這個過程是要先通過API Key和Secret Key獲取AccessToken,然后再通過AccessToken再將圖片通過POST請求發(fā)送給百度鑒別,然后返回結(jié)果。

獲取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(@"不是想要的結(jié)果,應(yīng)該是哪里出錯了,請注意檢查!");
NSLog(@"responseObject = %@",responseObject);
return;
}
*/
NSDictionary *dict = (NSDictionary *)responseObject;
NSString *accessToken = dict[@"access_token"];
_accessToken = dict[@"access_token"];
NSLog(@"%@",accessToken);
//調(diào)用判定二維碼的接口
[self getResultOfQRImageWithAFN1];
//[self getResultOfQRImageWithAFN2];
//[self getResultOfQRImageWithNSURLSession];
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"請求失敗--%@",error);
}];
}
PS :注意以下有一點要說一下
下面這句代碼意思是得到的響應(yīng)會序列化,何為序列化,序列化就是將其他的數(shù)據(jù)類型轉(zhuǎn)成二進制,所以一旦我們加上了這句話,那么我們在接收響應(yīng)的時候,就要反序列化,將二進制的NSData轉(zhuǎn)成其他響應(yīng)的數(shù)據(jù)類型
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
2>獲取AccessToken之后就需要去請求圖像識別接口了



首先請求是POST,我們再說下以下幾個要點:
要點一:
參數(shù)中有URL參數(shù),那么這個URL參數(shù)是直接拼接在URL后面的,這個要注意
要點二:
其中說到的Header表示的是我們傳給百度接口的是內(nèi)容Content-Type是Json格式的,并且是UTF-8編碼格式的,所以這個要特別注意,很多請求失敗可能是這些設(shè)置沒有設(shè)置好才導(dǎo)致的application/json;charset=utf-8
要點三:
請求體應(yīng)該怎么放,在AFN中如果是使用POST的URL方式的接口中的parameters這個參數(shù)其實就是請求體,并且這是id類型的,只要我們設(shè)置好請求的內(nèi)容格式然后這個參數(shù)傳入對應(yīng)格式的內(nèi)容即可
PS: 這里面就涉及到請求體,那怎么設(shè)置請求體呢在AFN中,有以下的三種方式:
方式一:
//這個是用Request方式
- (void)getResultOfQRImageWithAFN1{
//由于是POST,將一些憑證如AccessToken之類的拼接在一起,這種叫URL參數(shù),同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];
//創(chuàng)建請求request
NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:30];
//設(shè)置請求方式為POST
request.HTTPMethod = @"POST";
//設(shè)置請求內(nèi)容格式
[request setValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
//設(shè)置請求體參數(shù)
NSString *base64String = [self base64EncodeImageWithName:@"qrcode"];
NSArray *array = @[@"watermark"];
NSDictionary *params = @{@"image":base64String,
@"scenes": array};
//這是設(shè)置請求體,把參數(shù)放進請求體(這部分的參數(shù)也叫請求參數(shù))
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;
//以下結(jié)構(gòu)沒做放空處理,只為了盡快看到效果,請大家自行處理下
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的方式去請求而不是設(shè)置URL的方式,另外request是可以設(shè)置HTTPBody的,這樣更加的清晰
方式二:
//這個是用URL方式的
- (void)getResultOfQRImageWithAFN2{
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
//這句話加了之后返回的responseObject就是JSONData了,如果不加那就是正常的JSON可以直接轉(zhuǎn)成字典然后操作
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);
//以下結(jié)構(gòu)沒做放空處理
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: 大家要注意一下以下這兩句代碼的作用,第一句是設(shè)置請求上傳內(nèi)容是Json格式的,我試了以下如果單單設(shè)置第二句是會返回400Bad Request錯誤的,如果僅僅設(shè)置第一句是OK的
manager.requestSerializer = [AFJSONRequestSerializer serializer];
[manager.requestSerializer setValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
我看了下AFN的內(nèi)部實現(xiàn)其實已經(jīng)設(shè)置好了,大家有興趣可以去查看一下AFN的內(nèi)部源碼
+ (instancetype)serializer {
return [self serializerWithWritingOptions:(NSJSONWritingOptions)0];
}
+ (instancetype)serializerWithWritingOptions:(NSJSONWritingOptions)writingOptions
{
AFJSONRequestSerializer *serializer = [[self alloc] init];
serializer.writingOptions = writingOptions;
return serializer;
}
最終都會調(diào)用
- (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;
}
所以看到這里你會發(fā)現(xiàn)AFN封裝了好大一圈,一會兒在文末貼出純系統(tǒng)NSURLSession怎么實現(xiàn)
另外有兩個AFHTTPRequestSerializer的子類,一個是AFJSONRequestSerializer,這個是會把參數(shù)編碼成Json格式,然后設(shè)置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,并且設(shè)置為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可以直接轉(zhuǎn)成字典然后操作
//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.根據(jù)會話對象,創(chuàng)建一個Task任務(wù)
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(@"從服務(wù)器獲取到數(shù)據(jù)");
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.最后一步,執(zhí)行任務(wù),(一定調(diào)用這句話)
[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;
}
//這是我的一個工具類里面的方法,大家可以改成對象方法直接替換調(diào)用即可
+ (NSString*)dictionaryToJson:(NSDictionary *)dic
{
if (dic.allKeys.count == 0){
#ifdef DSDUBUG
NSLog(@"%@---%s",self.class,__FUNCTION__);
NSLog(@"您傳入的字典為空,無法轉(zhuǎn)換,請確保字典不為空?。?!");
#endif
return nil;
}
NSError *parseError = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:&parseError];
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
圖片:

文末總結(jié)
其實今天就講了一個很重要的問題,設(shè)置請求體,并且設(shè)置請求的內(nèi)容格式的問題。另外一個是我們其實可以對系統(tǒng)的方法NSURLSession進行封裝,系統(tǒng)的已經(jīng)很好了,其實有時間的話我們可以針對于我們的業(yè)務(wù)自己封裝會更好,因為AFN封裝了一層又一層太繞了...
AFN中的如果是GET請求,用URL方式的那個接口中的parameters參數(shù)其實就是我們需要拼接在URL后面的參數(shù),所以這個通常只需要轉(zhuǎn)一下UTF-8就好
AFN中的如果是POST請求,用URL方式的那個接口中的parameters參數(shù)其實就是我們需要上傳給后臺的參數(shù),因為要上傳所以需要設(shè)置一下內(nèi)容格式,一般上傳是上傳Json,也有上傳Form表單數(shù)據(jù)的比如圖片等等;另外這個URL參數(shù)就需要跟URL做一下拼接然后整體放置在以下接口中的newURLString:
[manager POST:newURLString parameters:params progress:nil success:
^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
//code here...
}];
以上?。?!