在实际的项目开发中,连接网络是每一款App必不可少的基本功能。对于客户端的网络请求而言,无非是有两个实现方向:使用网络请求框架或者不使用网络请求框架。在这篇博客中,我将用苹果自带的网络请求方式(不使用第三方框架)下对iOS网络请求方法做一个汇总。我将在之后的博客中介绍使用AFNetworking框架进行请求的实现。代码已经上传至:https://github.com/chenyufeng1991/iOS-NetworkRequest 。
【1.使用XML请求SOAP,用NSURLConnectionDelegate实现】
很多网络数据是通过SOAP请求的,http://www.webxml.com.cn/zh_cn/web_services.aspx 。上面有很多不错的接口,我们需要使用SOAP发送XML的方式来实现。我这里来调用手机号码归属地的接口,并OC实现,使用的是苹果自带的NSURLConnectionDelegate代理。请注意,有的WebService SOAP支持GET,有的支持POST,有的两者都支持。这个就要看服务端了。大家可以分别来进行测试。在该示例中,不支持GET方式,所以我就是用了POST。
实现代码如下:
#import "SOAP1ViewController.h"
@interface SOAP1ViewController ()
@property (strong, nonatomic) NSMutableData *webData;
@property (strong, nonatomic) NSURLConnection *conn;
@end
@implementation SOAP1ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self query:@"18888888888"];
}
-(void)query:(NSString*)phoneNumber{
// 创建SOAP消息,内容格式就是网站上提示的请求报文的主体实体部分 这里使用了SOAP1.2;
NSString *soapMsg = [NSString stringWithFormat:
@""
""
""
""
"%@ "
"%@ "
" "
" "
" ", phoneNumber, @""];
NSURL *url = [NSURL URLWithString: @"http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx"];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
NSString *msgLength = [NSString stringWithFormat:@"%lu", (unsigned long)[soapMsg length]];
[req addValue:@"application/soap+xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
[req addValue:msgLength forHTTPHeaderField:@"Content-Length"];
[req setHTTPMethod:@"POST"];
[req setHTTPBody: [soapMsg dataUsingEncoding:NSUTF8StringEncoding]];
self.conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
if (self.conn) {
self.webData = [NSMutableData data];
}
}
// 刚开始接受响应时调用
-(void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *) response{
[self.webData setLength: 0];
}
// 每接收到一部分数据就追加到webData中
-(void) connection:(NSURLConnection *)connection didReceiveData:(NSData *) data {
if(data != NULL){
[self.webData appendData:data];
}
}
// 出现错误时
-(void) connection:(NSURLConnection *)connection didFailWithError:(NSError *) error {
self.conn = nil;
self.webData = nil;
}
// 完成接收数据时调用
-(void) connectionDidFinishLoading:(NSURLConnection *) connection {
NSString *theXML = [[NSString alloc] initWithBytes:[self.webData mutableBytes]
length:[self.webData length]
encoding:NSUTF8StringEncoding];
// 打印出得到的XML
NSLog(@"返回的数据:%@", theXML);
}
@end
【2.使用XML请求SOAP,用NSURLConnection实现】
使用上述1的方法显得代码有点多,我们可以用最简单的NSURLConnection来实现,虽然现在已经不推荐使用NSURLConnection。但是我们还是来实现一下。这里采用异步请求的方式实现,就不会阻塞主线程。实现代码如下:
#import "SOAP2ViewController.h"
@interface SOAP2ViewController ()
@property (strong, nonatomic) NSMutableData *webData;
@property (strong, nonatomic) NSURLConnection *conn;
@end
@implementation SOAP2ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self query:@"18888888888"];
}
-(void)query:(NSString*)phoneNumber{
// 设置我们之后解析XML时用的关键字,与响应报文中Body标签之间的getMobileCodeInfoResult标签对应
// 创建SOAP消息,内容格式就是网站上提示的请求报文的主体实体部分 这里使用了SOAP1.2;
NSString *soapMsg = [NSString stringWithFormat:
@""
""
""
""
"%@ "
"%@ "
" "
" "
" ", phoneNumber, @""];
// 将这个XML字符串打印出来
// NSLog(@"%@", soapMsg);
// 创建URL,内容是前面的请求报文报文中第二行主机地址加上第一行URL字段
NSURL *url = [NSURL URLWithString: @"http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx"];
// 根据上面的URL创建一个请求
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
NSString *msgLength = [NSString stringWithFormat:@"%lu", (unsigned long)[soapMsg length]];
// 添加请求的详细信息,与请求报文前半部分的各字段对应
[req addValue:@"application/soap+xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
[req addValue:msgLength forHTTPHeaderField:@"Content-Length"];
// 设置请求行方法为POST,与请求报文第一行对应
[req setHTTPMethod:@"POST"];
// 将SOAP消息加到请求中
[req setHTTPBody: [soapMsg dataUsingEncoding:NSUTF8StringEncoding]];
// 创建连接
[NSURLConnection sendAsynchronousRequest:req queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
if (!connectionError) {
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"成功:%@",result);
}else{
NSLog(@"失败:%@",connectionError);
}
}];
}
@end
打印结果与上述1一样。
【3.使用XML请求SOAP,用NSURLSession实现】
目前苹果已经强烈推荐使用NSURLSession来进行网络请求了,虽然实现的代码量和难度 与NSURLConnection也差不多。实现代码如下:
#import "SOAP3ViewController.h"
@interface SOAP3ViewController ()
@property (strong, nonatomic) NSMutableData *webData;
@property (strong, nonatomic) NSURLConnection *conn;
@end
@implementation SOAP3ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self query:@"18888888888"];
}
-(void)query:(NSString*)phoneNumber{
// 设置我们之后解析XML时用的关键字,与响应报文中Body标签之间的getMobileCodeInfoResult标签对应
// 创建SOAP消息,内容格式就是网站上提示的请求报文的主体实体部分 这里使用了SOAP1.2;
NSString *soapMsg = [NSString stringWithFormat:
@""
""
""
""
"%@ "
"%@ "
" "
" "
" ", phoneNumber, @""];
// 将这个XML字符串打印出来
// NSLog(@"%@", soapMsg);
// 创建URL,内容是前面的请求报文报文中第二行主机地址加上第一行URL字段
NSURL *url = [NSURL URLWithString: @"http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx"];
// 根据上面的URL创建一个请求
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
NSString *msgLength = [NSString stringWithFormat:@"%lu", (unsigned long)[soapMsg length]];
// 添加请求的详细信息,与请求报文前半部分的各字段对应
[req addValue:@"application/soap+xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
[req addValue:msgLength forHTTPHeaderField:@"Content-Length"];
// 设置请求行方法为POST,与请求报文第一行对应
[req setHTTPMethod:@"POST"];
// 将SOAP消息加到请求中
[req setHTTPBody: [soapMsg dataUsingEncoding:NSUTF8StringEncoding]];
// 创建连接
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:req completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (!error) {
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"成功:%@",result);
}else{
NSLog(@"失败 : %@",error);
}
}];
[dataTask resume];
}
@end
打印结果与1相同。
【4.普通的Http请求,使用NSURLConnection】
最基本最普通的网络请求就是发送一个链接,链接里包含了需要发送的参数,同时请求方式可以是GET或者POST。这里我也是使用http://www.webxml.com.cn/zh_cn/web_services.aspx 进行号码归属地的请求。实现代码如下:
#import "HttpGet1ViewController.h"
@interface HttpGet1ViewController ()
@end
@implementation HttpGet1ViewController
- (void)viewDidLoad {
[super viewDidLoad];
/*
为什么要执行如下方法?
因为有的服务端要求把中文进行utf8编码,而我们的代码默认是unicode编码。必须要进行一下的转码,否则返回的可能为空,或者是其他编码格式的乱码了!
注意可以对整个url直接进行转码,而没必要对出现的每一个中文字符进行编码;
*/
//以下方法已经不推荐使用;
// NSString *urlStr = [@"http://v.juhe.cn/weather/index?format=2&cityname=北京&key=88e194ce72b455563c3bed01d5f967c5"stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
//建议使用这个方法stringByAddingPercentEncodingWithAllowedCharacters,不推荐使用stringByAddingPercentEscapesUsingEncoding;
NSString *urlStr2 = [@"http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx/getMobileCodeInfo?mobileCode=18888888888&userId=" stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
[self asynHttpGet:urlStr2];
}
- (NSString *)asynHttpGet:(NSString *)urlAsString{
NSURL *url = [NSURL URLWithString:urlAsString];
__block NSString *resault=@"";
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
[urlRequest setTimeoutInterval:30];
[urlRequest setHTTPMethod:@"GET"];
//以下方法已经被禁用;
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection
sendAsynchronousRequest:urlRequest
queue:queue
completionHandler:^(NSURLResponse *response,
NSData *data,
NSError *error) {
if ([data length] >0 &&
error == nil){
NSString *html = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
resault=[html copy];
NSLog(@"返回的服务器数据 = %@", html);
}
else if ([data length] == 0 &&
error == nil){
resault=@"Nothing was downloaded.";
NSLog(@"Nothing was downloaded.");
}
else if (error != nil){
resault=[NSString stringWithFormat:@"Error happened = %@", error];
NSLog(@"发生错误 = %@", error);
}
}];
return resault;
}
@end
【5.普通的Http请求,使用NSURL Session】
NSURLSession是对NSURLConnection的改进与优化。这是对4的改进。实现代码如下:
#import "HttpGet2ViewController.h"
@interface HttpGet2ViewController ()
@end
@implementation HttpGet2ViewController
- (void)viewDidLoad {
[super viewDidLoad];
/*
为什么要执行如下方法?
因为有的服务端要求把中文进行utf8编码,而我们的代码默认是unicode编码。必须要进行一下的转码,否则返回的可能为空,或者是其他编码格式的乱码了!
注意可以对整个url直接进行转码,而没必要对出现的每一个中文字符进行编码;
*/
//以下方法已经不推荐使用;
// NSString *urlStr = [@"http://v.juhe.cn/weather/index?format=2&cityname=北京&key=88e194ce72b455563c3bed01d5f967c5"stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
//建议使用这个方法stringByAddingPercentEncodingWithAllowedCharacters,不推荐使用stringByAddingPercentEscapesUsingEncoding;
NSString *urlStr2 = [@"http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx/getMobileCodeInfo?mobileCode=18888888888&userId=" stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
[self asynHttpGet:urlStr2];
}
- (NSString *)asynHttpGet:(NSString *)urlAsString{
NSURL *url = [NSURL URLWithString:urlAsString];
// __block NSString *resault=@"";
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
[urlRequest setTimeoutInterval:30];
[urlRequest setHTTPMethod:@"GET"];
//推荐使用这种请求方法;
NSURLSession *session = [NSURLSession sharedSession];
__block NSString *result = @"";
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:urlRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (!error) {
//没有错误,返回正确;
result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"返回正确:%@",result);
}else{
//出现错误;
NSLog(@"错误信息:%@",error);
}
}];
[dataTask resume];
return result;
}
@end
总结,网络请求虽然代码看起来有点多,但还是比较简单的,也基本上是固定的,可以直接拿过来用。我将在下一篇博客中介绍使用AFNetworking来进行请求。
最近开源的iOS应用,高仿印象笔记 https://github.com/chenyufeng1991/iOS-Oncenote 。欢迎大家点赞并关注项目进度。也可以安装到手机上试玩哦。
github主页:https://github.com/chenyufeng1991 。欢迎大家访问!