关于webservice
必须承认,在移动端做webservice请求是一件很痛苦的事情——复杂的请求体拼接,缓慢的xml格式解析。
但是,在实际工作中,还是不可避免的遇到一些这样的情况,那么,让我们尽量的做到代码优雅吧,不要让soap请求打乱了本来的节奏。
基于熟悉和优雅的诉求,webservice请求,我们还是基于的AFNetwork去做。
核心问题在于:AFNetwork2.0中的AFHTTPRequestOperation已被废弃,如何使用AFHTTPSessionManager去修改HTTPBody。
好了,下面让我们一起看一下
什么是soap
求助baidu(要是懒得看,跳过这一大段):
简单对象访问协议是交换数据的一种协议规范,是一种轻量的、简单的、基于XML(标准通用标记语言下的一个子集)的协议,它被设计成在WEB上交换结构化的和固化的信息。
基于类对象的传输协议。
SOAP封装(envelop),它定义了一个框架,描述消息中的内容是什么,是谁发送的,谁应当接受并处理它以及如何处理它们;
SOAP编码规则(encoding rules),它定义了一种序列化机制,用于表示应用程序需要使用的数据类型的实例;
SOAP RPC表示(RPC representation),它定了一个协定,用于表示远程过程调用和应答;
SOAP绑定(binding),它定义了SOAP使用哪种协议交换信息。使用HTTP/TCP/UDP协议都可以。
把SOAP绑定到HTTP提供了同时利用SOAP的样式和分散的灵活性的特点以及HTTP的丰富的特征库的优点。在HTTP上传送SOAP并不是说SOAP会覆盖现有的HTTP语义,而是HTTP上的SOAP语义会自然的映射到HTTP语义。在使用HTTP作为协议绑定的场合中,RPC请求映射到HTTP请求上,而RPC应答映射到HTTP应答。然而,在RPC上使用SOAP并不仅限于HTTP协议绑定。
——————自己理解————————:
这是一种webservice协议,本质来说,还是HTTP请求呗,还是那个老样子,分为HTTPHeader和HTTPBody
AFNetwork封装的很好,所以很多人可能已经不了解底层,AFNetwork以及苹果的请求体只让你看到配置部分,不给你看到xml格式的请求体。但是现在,soap请求,请你自己拼HTTPBody。
先去下载一个soapUI,这是一个非常强大的工具。把服务端给你的那个以?wdsl结尾的地址,放进去,可以读出所有的方法,以及你要去拼写的那个soap envelop(也就是信封)格式。点击运行,当然,你也可以查看运行结果。
soapui下载地址 http://www.soapui.org/
如果你必须要做soap接口请求了,务必下载!!!
1.关于NSMutableURLRequest
相信很多AFNetwork2.0访问webservice的例子中,都构建了NSMutableURLRequest,不赘述了,上代码
- (NSMutableURLRequest *)loadRequestWithParameter:(NSString *)parameter url:(NSString *)urlString methodName:(NSString *)methodName{
NSString *soapMessage =
[NSString stringWithFormat:
@"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:agen=\"http://agent.water.tjswfxkh/\" >"
"<soapenv:Body>"
"<agen:%@>"
"<arg0>%@</arg0>"
"</agen:%@>"
"</soapenv:Body>"
"</soapenv:Envelope>", methodName,parameter,methodName
];
// 将这个XML字符串打印出来
NSLog(@"%@", soapMessage);
// 创建URL,内容是前面的请求报文报文中第二行主机地址加上第一行URL字段
NSURL *url = [NSURL URLWithString:urlString];
// 根据上面的URL创建一个请求
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
NSString *msgLengt = [NSString stringWithFormat:@"%ld", [soapMessage length]];
// 添加请求的详细信息,与请求报文前半部分的各字段对应
[req addValue:@"application/soap+xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
[req addValue:msgLengt forHTTPHeaderField:@"Content-Length"];
// 设置请求行方法为POST,与请求报文第一行对应
[req setHTTPMethod:@"POST"];
// 将SOAP消息加到请求中
[req setHTTPBody: [soapMessage dataUsingEncoding:NSUTF8StringEncoding]];
return req;
}
代码洁癖,封装了一个方法做这个事情。入参分别是参数、地址、soap方法名。这样我们就可以复用了,对不对。毕竟在同一个项目中,xml标签那部分,不是那么容易改变。
soapMessage的来源,就是soapUI中,说过的信封请求体,你只要保证和它拼的一样就ok,setHTTPBody会将这部分,加到request里。
2.AFHTTPSessionManager
进行到这一步,那么,怎么把request加载到SessionManager呢
在AFNetwork2.0中关于AFHTTPRequestOperation,是这样子的
NSString *parameter = @"{\"endDate\":\"2015-06-01 08\",\"beginDate\":\"2015-06-01 08\"}";
NSString *urlStr = @"http://10.3.211.111/hw/cxf/water";
NSString *methodName = @"getSqInfo";
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:[self loadRequestWithParameter:parameter url:urlStr methodName:methodName]];
operation.responseSerializer = [AFXMLParserResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
//parse NSXMLParser object here if request successfull
if ([responseObject isKindOfClass:[NSXMLParser class]]) {
NSXMLParser *parser = (NSXMLParser *)responseObject;
NSError *parseError = nil;
NSDictionary *dict = [XMLReader dictionaryForNSXMLParser:parser error:&parseError];
NSLog(@"JSON: %@ - %@", responseObject,dict);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"Error: %@", error);
}];
[[NSOperationQueue mainQueue] addOperation:operation];
AFHTTPRequestOperation被废弃后,AFNetwork3.0结合AFHTTPSessionManager
精华的部分来了!!!!
NSString *parameter = @"{\"endDate\":\"2015-06-01 08\",\"beginDate\":\"2015-06-01 08\"}";
NSString *urlStr = @"http://10.3.211.111/hw/cxf/water";
NSString *methodName = @"getSqInfo";
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
//回复的序列化
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[[manager dataTaskWithRequest:[self loadRequestWithParameter:parameter url:urlStr methodName:methodName] completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
if (!error) {
NSData * data = (NSData *)responseObject;
NSXMLParser *parser = [[NSXMLParser alloc]initWithData:data];
NSError *parseError = nil;
NSDictionary *dict = [XMLReader dictionaryForNSXMLParser:parser error:&parseError];
NSLog(@"JSON: %@ - %@", responseObject,dict);
} else {
NSLog(@"Error: %@, %@, %@", error, response, responseObject);
}
}] resume];
务必要加这句话,不然在返回xml格式response时候,会报错,进到error状态,但实际上HTTP请求state code=200,即请求已经成功。
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
3.xml解析
上述代码经过本地服务的测试,已经跑通,不出意外的话,你会拿到一个NSData,解析后,是一个xml格式数据。
在这里用到了XMLReader,将其转换成了一个字典。
4.代码下载
演示代码已经上传,欢迎下载,希望能对大家有些帮助
https://github.com/Rita5969/afnetwork3.0-for-webservice
5.参考
非常非常感谢Pranoy C在stackoverflow贡献的答案
http://stackoverflow.com/questions/34561215/afnetworking-3-0-migration-how-to-post-with-headers-and-http-body
XMLReader-xml解析器
https://github.com/amarcadet/XMLReader
AFNetwork3.0迁移指南-官方Readme的中文翻译
http://www.jianshu.com/p/047463a7ce9b
———————————————————————————————————————————————————————————————————
如果大家对示例中的写法有好的建议,欢迎指正。后期如果有更好的方案,会继续更新。谢谢。
网友评论