本文主要是講一下我們?cè)谡?qǐng)求網(wǎng)絡(luò)的時(shí)候如何去配置相關(guān)的參數(shù)然后成功得到響應(yīng)。那本文以請(qǐng)求百度的API中圖像識(shí)別為例來(lái)說(shuō)一下該如何去配置庶香。
目的需求
我需要把一張包含二維碼的圖片通過(guò)百度的圖像識(shí)別接口來(lái)判定是否包含二維碼。
準(zhǔn)備工作
1>裝有Xcode的MAC電腦一臺(tái)
2>準(zhǔn)備一張二維碼圖片染服,這個(gè)隨便找一張枫甲,或者去草料二維碼制作一張也是OK的
3>百度云登錄注冊(cè),并創(chuàng)建一個(gè)圖像識(shí)別應(yīng)用副瀑,拿到API Key和Secret Key
4>百度對(duì)接文檔:圖像審核組合接口
開(kāi)始對(duì)接
1>創(chuàng)建工程項(xiàng)目,然后創(chuàng)建一個(gè)網(wǎng)絡(luò)請(qǐng)求恋谭。那么此處我選擇AFN糠睡,并且是手動(dòng)導(dǎo)入框架。首先這個(gè)過(guò)程是要先通過(guò)API Key和Secret Key獲取AccessToken,然后再通過(guò)AccessToken再將圖片通過(guò)POST請(qǐng)求發(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:@"點(diǎn)我獲取Token" forState:UIControlStateNormal];
[btn addTarget:self action:@selector(didClickBtn) forControlEvents:UIControlEventTouchUpInside];
}
- (void)didClickBtn{
[self getAccessToken];
}
- (void)getAccessToken{
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
//一但用了這個(gè)返回的那個(gè)responseObject就是NSData,如果不用就是簡(jiǎn)單的
//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":@"填寫(xiě)你的API Key",
@"client_secret":@"填寫(xiě)你的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)該是哪里出錯(cuò)了均抽,請(qǐ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(@"請(qǐng)求失敗--%@",error);
}];
}
PS :注意以下有一點(diǎn)要說(shuō)一下
下面這句代碼意思是得到的響應(yīng)會(huì)序列化其掂,何為序列化油挥,序列化就是將其他的數(shù)據(jù)類型轉(zhuǎn)成二進(jìn)制,所以一旦我們加上了這句話款熬,那么我們?cè)诮邮枕憫?yīng)的時(shí)候深寥,就要反序列化,將二進(jìn)制的NSData轉(zhuǎn)成其他響應(yīng)的數(shù)據(jù)類型
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
2>獲取AccessToken之后就需要去請(qǐng)求圖像識(shí)別接口了
首先請(qǐng)求是POST贤牛,我們?cè)僬f(shuō)下以下幾個(gè)要點(diǎn):
要點(diǎn)一:
參數(shù)中有URL參數(shù)惋鹅,那么這個(gè)URL參數(shù)是直接拼接在URL后面的,這個(gè)要注意
要點(diǎn)二:
其中說(shuō)到的Header表示的是我們傳給百度接口的是內(nèi)容Content-Type是Json格式的殉簸,并且是UTF-8編碼格式的闰集,所以這個(gè)要特別注意,很多請(qǐng)求失敗可能是這些設(shè)置沒(méi)有設(shè)置好才導(dǎo)致的application/json;charset=utf-8
要點(diǎn)三:
請(qǐng)求體應(yīng)該怎么放般卑,在AFN中如果是使用POST的URL方式的接口中的parameters這個(gè)參數(shù)其實(shí)就是請(qǐng)求體武鲁,并且這是id類型的,只要我們?cè)O(shè)置好請(qǐng)求的內(nèi)容格式然后這個(gè)參數(shù)傳入對(duì)應(yīng)格式的內(nèi)容即可
PS: 這里面就涉及到請(qǐng)求體蝠检,那怎么設(shè)置請(qǐng)求體呢在AFN中洞坑,有以下的三種方式:
方式一:
//這個(gè)是用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)建請(qǐng)求request
NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:30];
//設(shè)置請(qǐng)求方式為POST
request.HTTPMethod = @"POST";
//設(shè)置請(qǐng)求內(nèi)容格式
[request setValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
//設(shè)置請(qǐng)求體參數(shù)
NSString *base64String = [self base64EncodeImageWithName:@"qrcode"];
NSArray *array = @[@"watermark"];
NSDictionary *params = @{@"image":base64String,
@"scenes": array};
//這是設(shè)置請(qǐng)求體刽沾,把參數(shù)放進(jìn)請(qǐng)求體(這部分的參數(shù)也叫請(qǐng)求參數(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(@"請(qǐng)求成功---%@---%@",responseObject,[responseObject class]);
if (error) {
NSLog(@"%@",error);
return ;
}
NSDictionary *dict = (NSDictionary *)responseObject;
//以下結(jié)構(gòu)沒(méi)做放空處理,只為了盡快看到效果排拷,請(qǐng)大家自行處理下
NSDictionary *resultDict = dict[@"result"];
if (!resultDict) {
NSLog(@"result沒(méi)有值侧漓,很抱歉,估計(jì)錯(cuò)誤了监氢!");
return;
}
NSDictionary *newDict = resultDict[@"watermark"];
NSArray *resultArray = newDict[@"result"];
for (NSDictionary *dict in resultArray) {
if ([dict[@"type"] isEqualToString:@"QR code"]) {
NSLog(@"這絕壁有一張二維碼2颊帷!浪腐!");
}
}
}] resume];
}
PS: 這種方式是用manager直接通過(guò)request的方式去請(qǐng)求而不是設(shè)置URL的方式纵揍,另外request是可以設(shè)置HTTPBody的,這樣更加的清晰
方式二:
//這個(gè)是用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(@"請(qǐng)求成功---%@---%@",responseObject,[responseObject class]);
NSLog(@"成功T蠼鳌!特漩!");
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)沒(méi)做放空處理
NSDictionary *resultDict = response[@"result"];
if (!resultDict) {
NSLog(@"result沒(méi)有值吧雹,很抱歉,估計(jì)錯(cuò)誤了涂身!");
return;
}
NSDictionary *newDict = resultDict[@"watermark"];
NSArray *resultArray = newDict[@"result"];
for (NSDictionary *dict in resultArray) {
if ([dict[@"type"] isEqualToString:@"QR code"]) {
NSLog(@"這絕壁有一張二維碼P劬怼!蛤售!");
}
}
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"請(qǐng)求失敗--%@",error);
}];
}
PS: 大家要注意一下以下這兩句代碼的作用,第一句是設(shè)置請(qǐng)求上傳內(nèi)容是Json格式的丁鹉,我試了以下如果單單設(shè)置第二句是會(huì)返回400Bad Request錯(cuò)誤的,如果僅僅設(shè)置第一句是OK的
manager.requestSerializer = [AFJSONRequestSerializer serializer];
[manager.requestSerializer setValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
我看了下AFN的內(nèi)部實(shí)現(xiàn)其實(shí)已經(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;
}
最終都會(huì)調(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;
}
所以看到這里你會(huì)發(fā)現(xiàn)AFN封裝了好大一圈揣钦,一會(huì)兒在文末貼出純系統(tǒng)NSURLSession怎么實(shí)現(xiàn)
另外有兩個(gè)AFHTTPRequestSerializer的子類,一個(gè)是AFJSONRequestSerializer,這個(gè)是會(huì)把參數(shù)編碼成Json格式搜骡,然后設(shè)置Content-Type為application/json,但是并沒(méi)有進(jìn)行UTF-8編碼佑女,所以這個(gè)還是要注意一下自己編碼
/**
`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
另一個(gè)是AFPropertyListRequestSerializer,這個(gè)可以猜一下记靡,其實(shí)就是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];
}
對(duì)比原生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.獲得會(huì)話對(duì)象
NSURLSession *session = [NSURLSession sharedSession];
// 4.根據(jù)會(huì)話對(duì)象摸吠,創(chuàng)建一個(gè)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(@"失敗:炕ā4缌 !");
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);
//這里記得把這個(gè)值取出來(lái)紊选,很深啼止,哈哈哈...
}
}];
//5.最后一步道逗,執(zhí)行任務(wù),(一定調(diào)用這句話)
[sessionDataTask resume];
}
附件
其中用到了一個(gè)私有方法:
- (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;
}
//這是我的一個(gè)工具類里面的方法献烦,大家可以改成對(duì)象方法直接替換調(diào)用即可
+ (NSString*)dictionaryToJson:(NSDictionary *)dic
{
if (dic.allKeys.count == 0){
#ifdef DSDUBUG
NSLog(@"%@---%s",self.class,__FUNCTION__);
NSLog(@"您傳入的字典為空滓窍,無(wú)法轉(zhuǎn)換,請(qǐng)確保字典不為空9恰@艉弧!");
#endif
return nil;
}
NSError *parseError = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:&parseError];
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
圖片:
文末總結(jié)
其實(shí)今天就講了一個(gè)很重要的問(wèn)題即横,設(shè)置請(qǐng)求體噪生,并且設(shè)置請(qǐng)求的內(nèi)容格式的問(wèn)題。另外一個(gè)是我們其實(shí)可以對(duì)系統(tǒng)的方法NSURLSession進(jìn)行封裝东囚,系統(tǒng)的已經(jīng)很好了跺嗽,其實(shí)有時(shí)間的話我們可以針對(duì)于我們的業(yè)務(wù)自己封裝會(huì)更好,因?yàn)锳FN封裝了一層又一層太繞了...
AFN中的如果是GET請(qǐng)求,用URL方式的那個(gè)接口中的parameters參數(shù)其實(shí)就是我們需要拼接在URL后面的參數(shù)舔庶,所以這個(gè)通常只需要轉(zhuǎn)一下UTF-8就好
AFN中的如果是POST請(qǐng)求,用URL方式的那個(gè)接口中的parameters參數(shù)其實(shí)就是我們需要上傳給后臺(tái)的參數(shù)抛蚁,因?yàn)橐蟼魉孕枰O(shè)置一下內(nèi)容格式,一般上傳是上傳Json惕橙,也有上傳Form表單數(shù)據(jù)的比如圖片等等瞧甩;另外這個(gè)URL參數(shù)就需要跟URL做一下拼接然后整體放置在以下接口中的newURLString:
[manager POST:newURLString parameters:params progress:nil success:
^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
//code here...
}];
以上!C逐小肚逸!