IOS网络请求之NSURLSession使用详解
前言:
无论是Android还是ios都离不开与服务器交互,这就必须用到网络请求,记得在2013年做iOS的时候那时候用的ASIHTTPRequest框架,现在重新捡起iOS的时候ASIHTTPRequest已经停止维护,大家都在用AFNetWorking作为首选网络请求框架,之前的ASIHTTPRequest是基于NSURLConnection类实现的,早期的AFNetWorking也是基于NSURLConnection实现,后来iOS9之后已经放弃了NSURLConnection,开始使用iOS7之后推出的NSURLSession,本着追根溯源的原则,首先学习一下NSURLSession的实现网络请求,然后再去学习AFNetWorking。
了解NSURLSession
NSURLSession是2013年iOS7发布的用于替代NSURLConnection的,iOS9之后NSURLConnection彻底推出历史舞台。其使用起来非常方便,今天使用NSURLConnection分别实现了get、post、表单提交、文件上传、文件下载,让我这个以Android开发为主的屌丝程序员赞叹不已,根据NSURLSession会话对象创建一个请求Task,然后执行该Task即可,包括缓存、会话周期,多线程任务iOS都已经在sdk层面封装完毕,不过比较遗憾的时NSURLSession只提供了异步请求方式而没有提供同步请求方式。接下来我们来如何实现网络请求。
NSURLSession使用
我们首先以一个简单的get请求为例开始。
1.)首先构造一个NSURL请求资源地址
//构造URL资源地址 NSURL*url=[NSURLURLWithString:@http://api.nohttp.net/method?name=yanzhenjie&pwd=123];
2.)创建一个NSRequest请求对象
//创建Request请求 NSMutableURLRequest*request=[NSMutableURLRequestrequestWithURL:url]; //配置Request请求 //设置请求方法 [requestsetHTTPMethod:@"GET"]; //设置请求超时默认超时时间60s [requestsetTimeoutInterval:10.0]; //设置头部参数 [requestaddValue:@"gzip"forHTTPHeaderField:@"Content-Encoding"]; //或者下面这种方式添加所有请求头信息 request.allHTTPHeaderFields=@{@"Content-Encoding":@"gzip"}; //设置缓存策略 [requestsetCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
根据需求添加不用的设置,比如请求方式、超时时间、请求头信息,这里重点介绍下缓存策略:
- NSURLRequestUseProtocolCachePolicy=0//默认的缓存策略,如果缓存不存在,直接从服务端获取。如果缓存存在,会根据response中的Cache-Control字段判断下一步操作,如:Cache-Control字段为must-revalidata,则询问服务端该数据是否有更新,无更新的话直接返回给用户缓存数据,若已更新,则请求服务端.
- NSURLRequestReloadIgnoringLocalCacheData=1//忽略本地缓存数据,直接请求服务端.
- NSURLRequestIgnoringLocalAndRemoteCacheData=4//忽略本地缓存,代理服务器以及其他中介,直接请求源服务端.
- NSURLRequestReloadIgnoringCacheData=NSURLRequestReloadIgnoringLocalCacheData
- NSURLRequestReturnCacheDataElseLoad=2//有缓存就使用,不管其有效性(即忽略Cache-Control字段),无则请求服务端.
- NSURLRequestReturnCacheDataDontLoad=3//只加载本地缓存.没有就失败.(确定当前无网络时使用)
- NSURLRequestReloadRevalidatingCacheData=5//缓存数据必须得得到服务端确认有效才使用
3.)创建NSURLSession会话对象
可以通过采用iOS共享Session的方式
//采用苹果提供的共享session NSURLSession*sharedSession=[NSURLSessionsharedSession];
可以通过NSURLSessionConfiguration方式配置不同的NSURLSession
//构造NSURLSessionConfiguration NSURLSessionConfiguration*configuration=[NSURLSessionConfigurationdefaultSessionConfiguration]; //构造NSURLSession,网络会话; NSURLSession*session=[NSURLSessionsessionWithConfiguration:configuration];
通过NSURLSessionConfiguration提供了三种创建NSURLSession的方式
- defaultSessionConfiguration//默认配置使用的是持久化的硬盘缓存,存储证书到用户钥匙链。存储cookie到shareCookie。
- ephemeralSessionConfiguration//不使用永久持存cookie、证书、缓存的配置,最佳优化数据传输。
- backgroundSessionConfigurationWithIdentifier//可以上传下载HTTP和HTTPS的后台任务(程序在后台运行)。
在后台时,将网络传输交给系统的单独的一个进程,即使app挂起、推出甚至崩溃照样在后台执行。
也可以通过NSURLSessionConfiguration统一设置超时时间、请求头等信息
//构造NSURLSessionConfiguration NSURLSessionConfiguration*configuration=[NSURLSessionConfigurationdefaultSessionConfiguration]; //设置请求超时为10秒钟 configuration.timeoutIntervalForRequest=10; //在蜂窝网络情况下是否继续请求(上传或下载) configuration.allowsCellularAccess=NO; //配置请求头 configuration.HTTPAdditionalHeaders=@{@"Content-Encoding":@"gzip"};
4.)创建NSURLSessionTask对象,然后执行
//构造NSURLSessionTask,会话任务; NSURLSessionDataTask*task=[sessiondataTaskWithRequest:requestcompletionHandler:^(NSData*_Nullabledata,NSURLResponse*_Nullableresponse,NSError*_Nullableerror){ //请求失败,打印错误信息 if(error){ NSLog(@"geterror:%@",error.localizedDescription); } //请求成功,解析数据 else{ //JSON数据格式解析 idobject=[NSJSONSerializationJSONObjectWithData:dataoptions:NSJSONReadingMutableLeaveserror:&error]; //判断是否解析成功 if(error){ NSLog(@"geterror:%@",error.localizedDescription); }else{ NSLog(@"getsuccess:%@",object); //解析成功,处理数据,通过GCD获取主队列,在主线程中刷新界面。 dispatch_async(dispatch_get_main_queue(),^{ //刷新界面.... }); } } }];
iOS为了适应不同的应用场景提供了不同类型的NSSessionTask
- NSURLSessionDataTask //一般的get、post等请求
- NSURLSessionUploadTask//用于上传文件或者数据量比较大的请求
- NSURLSessionDownloadTask//用于下载文件或者数据量比较大的请求
- NSURLSessionStreamTask//建立一个TCP/IP连接的主机名和端口或一个网络服务对象。
task的三个函数
- -(void)suspend;//暂停
- -(void)resume;//开始或者恢复
- -(void)cancel;//关闭任务
NSURLSession其他请求示例
1.)post请求
//1、创建URL资源地址 NSURL*url=[NSURLURLWithString:@"http://api.nohttp.net/postBody"]; //2、创建Reuest请求 NSMutableURLRequest*request=[NSMutableURLRequestrequestWithURL:url]; //3、配置Request //设置请求超时 [requestsetTimeoutInterval:10.0]; //设置请求方法 [requestsetHTTPMethod:@"POST"]; //设置头部参数 [requestaddValue:@"gzip"forHTTPHeaderField:@"Content-Encoding"]; //4、构造请求参数 //4.1、创建字典参数,将参数放入字典中,可防止程序员在主观意识上犯错误,即参数写错。 NSDictionary*parametersDict=@{@"name":@"yanzhenjie",@"pwd":@"123"}; //4.2、遍历字典,以“key=value&”的方式创建参数字符串。 NSMutableString*parameterString=[[NSMutableStringalloc]init]; intpos=0; for(NSString*keyinparametersDict.allKeys){ //拼接字符串 [parameterStringappendFormat:@"%@=%@",key,parametersDict[key]]; if(pos<parametersDict.allKeys.count-1){ [parameterStringappendString:@"&"]; } pos++; } //4.3、NSString转成NSData数据类型。 NSData*parametersData=[parameterStringdataUsingEncoding:NSUTF8StringEncoding]; //5、设置请求报文 [requestsetHTTPBody:parametersData]; //6、构造NSURLSessionConfiguration NSURLSessionConfiguration*configuration=[NSURLSessionConfigurationdefaultSessionConfiguration]; //7、创建网络会话 NSURLSession*session=[NSURLSessionsessionWithConfiguration:configuration]; //8、创建会话任务 NSURLSessionDataTask*task=[sessiondataTaskWithRequest:requestcompletionHandler:^(NSData*_Nullabledata,NSURLResponse*_Nullableresponse,NSError*_Nullableerror){ //10、判断是否请求成功 if(error){ NSLog(@"posterror:%@",error.localizedDescription); }else{ //如果请求成功,则解析数据。 idobject=[NSJSONSerializationJSONObjectWithData:dataoptions:NSJSONReadingMutableLeaveserror:&error]; //11、判断是否解析成功 if(error){ NSLog(@"posterror:%@",error.localizedDescription); }else{ //解析成功,处理数据,通过GCD获取主队列,在主线程中刷新界面。 NSLog(@"postsuccess:%@",object); dispatch_async(dispatch_get_main_queue(),^{ //刷新界面... }); } } }]; //9、执行任务 [taskresume];
2.)附带表单参数文件上传
NSDate*dat=[NSDatedateWithTimeIntervalSinceNow:0]; NSTimeIntervala=[dattimeIntervalSince1970]; NSString*fileName=[NSStringstringWithFormat:@"file_%0.f.txt",a]; [FileUtilswriteDataToFile:fileNamedata:[@"upload_file_to_server"dataUsingEncoding:NSUTF8StringEncoding]]; //以流的方式上传,大小理论上不受限制,但应注意时间 //1、创建URL资源地址 NSURL*url=[NSURLURLWithString:@"http://api.nohttp.net/upload"]; //2、创建Reuest请求 NSMutableURLRequest*request=[NSMutableURLRequestrequestWithURL:url]; //3、配置Request //设置Body值方法二,这种方法比较原始,不常用,不过可以用来上传参数和文件 NSString*BOUNDARY=@"whoislcj";//表单分界线可以自定义任意值 [requestsetValue:[@"multipart/form-data;boundary="stringByAppendingString:BOUNDARY]forHTTPHeaderField:@"Content-Type"]; //文件上传使用post [requestsetHTTPMethod:@"POST"]; //设置请求超时 [requestsetTimeoutInterval:30.0f]; //用于存放二进制数据流 NSMutableData*body=[NSMutableDatadata]; //追加一个普通表单参数name=yanzhenjie NSString*nameParam=[NSStringstringWithFormat:@"--%@\r\nContent-Disposition:form-data;name=\"%@\"\r\n\r\n%@\r\n",BOUNDARY,@"name",@"yanzhenjie",nil]; [bodyappendData:[nameParamdataUsingEncoding:NSUTF8StringEncoding]]; //追加一个普通表单参数pwd=123 NSString*pwdParam=[NSStringstringWithFormat:@"--%@\r\nContent-Disposition:form-data;name=\"%@\"\r\n\r\n%@\r\n",BOUNDARY,@"pwd",@"123",nil]; [bodyappendData:[pwdParamdataUsingEncoding:NSUTF8StringEncoding]]; //追加一个文件表单参数 //Content-Disposition:form-data;name="<服务器端需要知道的名字>";filename="<服务器端这个传上来的文件名>" //Content-Type:application/octet-stream--根据不同的文件类型选择不同的值 NSString*file=[NSStringstringWithFormat:@"--%@\r\nContent-Disposition:form-data;name=\"%@\";filename=\"%@\"\r\nContent-Type:application/octet-stream\r\n\r\n",BOUNDARY,@"headUrl",fileName,nil]; [bodyappendData:[filedataUsingEncoding:NSUTF8StringEncoding]]; //获取file路径 NSString*filePath=[FileUtilsgetFilePath:fileName]; NSData*data=[NSDatadataWithContentsOfFile:filePath]; //追加文件二进制数据 [bodyappendData:data]; [bodyappendData:[@"\r\n"dataUsingEncoding:NSUTF8StringEncoding]]; //结束分割线 NSString*endString=[NSStringstringWithFormat:@"--%@--",BOUNDARY]; [bodyappendData:[endStringdataUsingEncoding:NSUTF8StringEncoding]]; //创建会话 NSURLSession*session=[NSURLSessionsharedSession]; //3.开始上传request的bodydata将被忽略,而由fromData提供 NSURLSessionUploadTask*uploadTask=[sessionuploadTaskWithRequest:requestfromData:bodycompletionHandler:^(NSData*_Nullabledata,NSURLResponse*_Nullableresponse,NSError*_Nullableerror){ if(error){ NSLog(@"uploaderror:%@",error); }else{ NSLog(@"uploadsuccess:%@",[NSJSONSerializationJSONObjectWithData:dataoptions:NSJSONReadingMutableLeaveserror:&error]); dispatch_async(dispatch_get_main_queue(),^{ //刷新界面... }); } }]; //执行任务 [uploadTaskresume];
3.)文件下载
//创建url NSString*urlStr=@"http://images2015.cnblogs.com/blog/950883/201701/950883-20170105104233581-62069155.png"; NSURL*Url=[NSURLURLWithString:urlStr]; //创建请求 NSMutableURLRequest*request=[NSMutableURLRequestrequestWithURL:Url]; //设置请求超时 [requestsetTimeoutInterval:30.0]; //创建会话 NSURLSession*session=[NSURLSessionsharedSession]; NSURLSessionDownloadTask*downLoadTask=[sessiondownloadTaskWithRequest:requestcompletionHandler:^(NSURL*_Nullablelocation,NSURLResponse*_Nullableresponse,NSError*_Nullableerror){ if(!error){ NSLog(@"downloadsucess:%@",location); NSData*data=[NSDatadataWithContentsOfURL:location]; UIImage*image=[UIImageimageWithData:data]; dispatch_async(dispatch_get_main_queue(),^{ //刷新界面... UIImageView*imageView=[[UIImageViewalloc]init]; imageView.image=image; [self.viewaddSubview:imageView]; [imageViewmas_makeConstraints:^(MASConstraintMaker*make){ make.center.equalTo(self.view); make.size.mas_equalTo(CGSizeMake(300,300)); }]; }); }else{ NSLog(@"downloaderror:%@",error.localizedDescription); } }]; //启动任务 [downLoadTaskresume];
总结:
今天学习了iOS底层如何实现网络请求的,为了开发效率还得依靠优秀的第三方开源框架,以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。