前言
我们在开发中一般很少接触到去掉webservice服务,最近的项目就是调用的webservice,所以这篇文章主要说明了怎么去调webservice服务以及遇到的一些问题。
开始调用服务
先贴代码,然后开始说明其中的东西
封装一个网络请求类DataCenterBaseAPIManager 继承NSObject
- DataCenterBaseAPIManager.h文件
#import <Foundation/Foundation.h>
@interface DataCenterBaseAPIManager : NSObject
@property (nonatomic, strong) void(^Block)(NSDictionary *);//返回请求下来的数据
/**
* 执行网络请求
*
* @param urlString URL
* @param parameter 请求体
*/
-(void)startRquestURL:(NSString *)urlString parameters:(NSString *)parameter soapActionURL:(NSString *)actionURL;
@end
- DataCenterBaseAPIManager.m文件
#import "XMLReader.h"
#import "DataCenterBaseAPIManager.h"
@interface DataCenterBaseAPIManager()<NSXMLParserDelegate,NSURLConnectionDelegate>
@property (strong, nonatomic) NSMutableData *webData;
@property (strong, nonatomic) NSMutableString *soapResults;
@property (strong, nonatomic) NSXMLParser *xmlParser;
@property (nonatomic) BOOL elementFound;
@property (strong, nonatomic) NSString *matchingElement;
@property (strong, nonatomic) NSURLConnection *conn;
@end
@implementation DataCenterBaseAPIManager
@synthesize webData;
@synthesize soapResults;
@synthesize xmlParser;
@synthesize elementFound;
@synthesize matchingElement;
@synthesize conn;
-(void)startRquestURL:(NSString *)urlString parameters:(NSString *)parameter soapActionURL:(NSString *)actionURL
{
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0];
req.cachePolicy = NSURLRequestUseProtocolCachePolicy;
NSString *msgLength = [NSString stringWithFormat:@"%zi",[parameter length]];
[req addValue:@"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
[req addValue:msgLength forHTTPHeaderField:@"Content-Length"];
[req setHTTPMethod:@"POST"];
[req setHTTPBody: [parameter dataUsingEncoding:NSUTF8StringEncoding]];
[req addValue:actionURL forHTTPHeaderField:@"SOAPAction"];
conn =[[NSURLConnection alloc] initWithRequest:req delegate:self];
if (conn) {
webData = [NSMutableData data];
}
}
-(void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *) response{
[webData setLength: 0];
}
// 每接收到一部分数据就追加到webData中
-(void) connection:(NSURLConnection *)connection didReceiveData:(NSData *) data {
[webData appendData:data];
}
// 出现错误时
-(void) connection:(NSURLConnection *)connection didFailWithError:(NSError *) error {
conn = nil;
webData = nil;
}
// 完成接收数据时调用
-(void) connectionDidFinishLoading:(NSURLConnection *) connection {
NSString *theXML = [[NSString alloc] initWithBytes:[webData mutableBytes]
length:[webData length]
encoding:NSUTF8StringEncoding];
NSError *parseError = nil;
xmlParser = [[NSXMLParser alloc] initWithData: webData];
xmlParser.delegate = self;
[xmlParser setShouldResolveExternalEntities: YES];
[xmlParser parse];
NSDictionary *dic = [XMLReader dictionaryForXMLString:theXML error:&parseError];
if (self.Block) {
self.Block(dic);
}
}
-(void) parser:(NSXMLParser *) parser didStartElement:(NSString *) elementName namespaceURI:(NSString *) namespaceURI qualifiedName:(NSString *) qName attributes:(NSDictionary *) attributeDict {
if ([elementName isEqualToString:matchingElement]) {
if (!soapResults) {
soapResults = [[NSMutableString alloc] init];
}
elementFound = YES;
}
}
// 追加找到的元素值,一个元素值可能要分几次追加
-(void)parser:(NSXMLParser *) parser foundCharacters:(NSString *)string {
if (elementFound) {
[soapResults appendString: string];
}
}
// 结束解析这个元素名
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:matchingElement]) {
elementFound = FALSE;
// 强制放弃解析
[xmlParser abortParsing];
}
}
// 解析整个文件结束后
- (void)parserDidEndDocument:(NSXMLParser *)parser {
if (soapResults) {
soapResults = nil;
}
}
// 出错时,例如强制结束解析
- (void) parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
if (soapResults) {
soapResults = nil;
}
}
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
return YES;
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([challenge previousFailureCount] > 0) {
NSError *failure = [challenge error];
NSLog(@"Can't authenticate:%@", [failure localizedDescription]);
[[challenge sender] cancelAuthenticationChallenge:challenge];
return;
}
NSURLCredential *newCred = [NSURLCredential credentialWithUser:@"xxx" password:@"xxx" persistence:NSURLCredentialPersistenceNone];
[[challenge sender] useCredential:newCred forAuthenticationChallenge:challenge];
}
@end
1.DataCenterBaseAPIManager.h文件
- block:当异步请求结束之后将xml文件解析为字典的形式传值回来
- 方法:
1.有一个soapActionURL字段必须注意下,因为在有的服务中这个字段的值是nil,但是一部分请求中这个字段的值不为nil。
2.怎么去获得这个字段的值?
我的方法是在浏览器中打开你要请求这个服务的地址,然后在其中找到SoapAction字段,将后面的字段值拷贝下来放在网络请求中即可。
如下图所示:
没有soapAction的情况
没有SoapAction.png
有soapAction的情况(图中有两个字段,不需要都选上,需要选择你对应的请求的字段值)有soapAction.png
2.DataCenterBaseAPIManager.m文件
主要讲几个函数,其他基本有注释,将代码Copy下来就能看懂
-(void)startRquestURL:(NSString *)urlString parameters:(NSString *)parameter soapActionURL:(NSString *)actionURL方法
与其他的请求不一样的地方就是需要给request添加一些键值
[req addValue:@"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
[req addValue:msgLength forHTTPHeaderField:@"Content-Length"];
[req setHTTPMethod:@"POST"];
[req setHTTPBody: [parameter dataUsingEncoding:NSUTF8StringEncoding]];
[req addValue:actionURL forHTTPHeaderField:@"SOAPAction"];
-(void) connectionDidFinishLoading:(NSURLConnection *) connection方法
在其中解析请求下来的XML
NSString *theXML = [[NSString alloc] initWithBytes:[webData mutableBytes]
length:[webData length]
encoding:NSUTF8StringEncoding];
NSError *parseError = nil;
xmlParser = [[NSXMLParser alloc] initWithData: webData];
xmlParser.delegate = self;
[xmlParser setShouldResolveExternalEntities: YES];
[xmlParser parse];
NSDictionary *dic = [XMLReader dictionaryForXMLString:theXML error:&parseError];
if (self.Block) {
self.Block(dic);
}
-(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace方法
-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge方法
这两个方法添加主要是用来访问HTTPS网站的,所以只需要将代码中的"xxx"改成你要访问的用户名密码即可。
XMLReader
在解析XML的时候我用到了XMLReader类,代码如下
XMLReader.h
#import <Foundation/Foundation.h>
enum {
XMLReaderOptionsProcessNamespaces = 1 << 0, // Specifies whether the receiver reports the namespace and the qualified name of an element.
XMLReaderOptionsReportNamespacePrefixes = 1 << 1, // Specifies whether the receiver reports the scope of namespace declarations.
XMLReaderOptionsResolveExternalEntities = 1 << 2, // Specifies whether the receiver reports declarations of external entities.
};
typedef NSUInteger XMLReaderOptions;
@interface XMLReader : NSObject<NSXMLParserDelegate>
+ (NSDictionary *)dictionaryForXMLData:(NSData *)data error:(NSError **)errorPointer;
+ (NSDictionary *)dictionaryForXMLString:(NSString *)string error:(NSError **)errorPointer;
+ (NSDictionary *)dictionaryForXMLData:(NSData *)data options:(XMLReaderOptions)options error:(NSError **)errorPointer;
+ (NSDictionary *)dictionaryForXMLString:(NSString *)string options:(XMLReaderOptions)options error:(NSError **)errorPointer;
@end
XMLReader.m
//将xcode升级到8.1之后会出现一些打印语句,并且可能在进行xml解析的时候打印不出信息,并且有个delloc错误。下面的代码先做下处理
#ifdef DEBUG
#define NSLog(FORMAT, ...) fprintf(stderr,"%s\n",[[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
#else
#define NSLog(...)
#endif
NSString *const kXMLReaderTextNodeKey = @"text";
NSString *const kXMLReaderAttributePrefix = @"@";
@interface XMLReader ()
@property (nonatomic, strong) NSMutableArray *dictionaryStack;
@property (nonatomic, strong) NSMutableString *textInProgress;
@property (nonatomic, strong) NSError *errorPointer;
@end
@implementation XMLReader
+ (NSDictionary *)dictionaryForXMLData:(NSData *)data error:(NSError **)error
{
XMLReader *reader = [[XMLReader alloc] initWithError:error];
NSDictionary *rootDictionary = [reader objectWithData:data options:0];
return rootDictionary;
}
+ (NSDictionary *)dictionaryForXMLString:(NSString *)string error:(NSError **)error
{
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
return [XMLReader dictionaryForXMLData:data error:error];
}
+ (NSDictionary *)dictionaryForXMLData:(NSData *)data options:(XMLReaderOptions)options error:(NSError **)error
{
XMLReader *reader = [[XMLReader alloc] initWithError:error];
NSDictionary *rootDictionary = [reader objectWithData:data options:options];
return rootDictionary;
}
+ (NSDictionary *)dictionaryForXMLString:(NSString *)string options:(XMLReaderOptions)options error:(NSError **)error
{
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
return [XMLReader dictionaryForXMLData:data options:options error:error];
}
#pragma mark - Parsing
- (id)initWithError:(NSError **)error
{
self = [super init];
if (self)
{
self.errorPointer = *error;
}
return self;
}
- (NSDictionary *)objectWithData:(NSData *)data options:(XMLReaderOptions)options
{
// Clear out any old data
self.dictionaryStack = [[NSMutableArray alloc] init];
self.textInProgress = [[NSMutableString alloc] init];
// Initialize the stack with a fresh dictionary
[self.dictionaryStack addObject:[NSMutableDictionary dictionary]];
// Parse the XML
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
[parser setShouldProcessNamespaces:(options & XMLReaderOptionsProcessNamespaces)];
[parser setShouldReportNamespacePrefixes:(options & XMLReaderOptionsReportNamespacePrefixes)];
[parser setShouldResolveExternalEntities:(options & XMLReaderOptionsResolveExternalEntities)];
parser.delegate = self;
BOOL success = [parser parse];
// Return the stack's root dictionary on success
if (success)
{
NSDictionary *resultDict = [self.dictionaryStack objectAtIndex:0];
return resultDict;
}
return nil;
}
#pragma mark - NSXMLParserDelegate methods
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
// Get the dictionary for the current level in the stack
NSMutableDictionary *parentDict = [self.dictionaryStack lastObject];
// Create the child dictionary for the new element, and initilaize it with the attributes
NSMutableDictionary *childDict = [NSMutableDictionary dictionary];
[childDict addEntriesFromDictionary:attributeDict];
// If there's already an item for this key, it means we need to create an array
id existingValue = [parentDict objectForKey:elementName];
if (existingValue)
{
NSMutableArray *array = nil;
if ([existingValue isKindOfClass:[NSMutableArray class]])
{
// The array exists, so use it
array = (NSMutableArray *) existingValue;
}
else
{
// Create an array if it doesn't exist
array = [NSMutableArray array];
[array addObject:existingValue];
// Replace the child dictionary with an array of children dictionaries
[parentDict setObject:array forKey:elementName];
}
// Add the new child dictionary to the array
[array addObject:childDict];
}
else
{
// No existing value, so update the dictionary
[parentDict setObject:childDict forKey:elementName];
}
// Update the stack
[self.dictionaryStack addObject:childDict];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
// Update the parent dict with text info
NSMutableDictionary *dictInProgress = [self.dictionaryStack lastObject];
// Set the text property
if ([self.textInProgress length] > 0)
{
// trim after concatenating
NSString *trimmedString = [self.textInProgress stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[dictInProgress setObject:[trimmedString mutableCopy] forKey:kXMLReaderTextNodeKey];
// Reset the text
self.textInProgress = [[NSMutableString alloc] init];
}
// Pop the current dict
[self.dictionaryStack removeLastObject];
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
// Build the text value
[self.textInProgress appendString:string];
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
// Set the error pointer to the parser's error object
self.errorPointer = parseError;
}
@end
写请求体
我直接贴一个我写的请求体
[NSString stringWithFormat:@"<soap12:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap12=\"http://www.w3.org/2003/05/soap-envelope\"><soap12:Body><ExcuterSql xmlns=\"http://tempuri.org/\"><sql>select * from T06_MONITOR_PARTITEM1 where site_ssc_id = 32</sql></ExcuterSql></soap12:Body></soap12:Envelope>"]
[NSString stringWithFormat:@"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><service xmlns=\"http://webservice.datacenter.ylidc/GetImplementationOfProgressInfo\"><arg0 xmlns=\"\">0</arg0><arg1 xmlns=\"\">99999</arg1><arg2 xmlns=\"\">12LHK</arg2><arg3 xmlns=\"\">雅江县</arg3><arg4 xmlns=\"\">201402</arg4><arg5 xmlns=\"\">201802</arg5></service></s:Body></s:Envelope>"]
请求体差不多都是这么写的
- 注意区分两个请求体的不同两个请求的soap版本不一样,一个是1.1,一个是1.2,所以写的请求体也不一样。要查看具体是哪个版本?可以将请求的地址在浏览器中打开,观察对比。
- 请求体中每个双引号 都必须加上斜杆,例如这样
\"\"
- 请求体的写法也可以查看这个博客(相对来说比较清晰,原理差不多,但是可能不适用了)
http://www.cnblogs.com/ygm900/p/3490072.html
执行请求
例子:在需要网络请求的地方添加这些
1.添加属性
@property (nonatomic,strong)DataCenterBaseAPIManager *APIManager;
2.懒加载
-(DataCenterBaseAPIManager *)APIManager{
if (!_APIManager) {
_APIManager = [[DataCenterBaseAPIManager new];
}
return _APIManager;
}
3.请求
//url为请求的地址 soapBody 为请求体
[self.APIManager startRquestURL:url parameters:soapBody soapActionURL:@""];
//回调
self.APIManager.Block = ^(NSDictionary *dictionary){
//查看dictionary
//根据请求的数据刷新视图等等。
}
总结
这样子是能够调用服务的,但是有一些问题,例如当我同时执行多个网络请求的时候,我想得到的请求结果需要按顺序展示到视图上。因为这里是异步执行的,所以根本不能得到想要的结果。
让我想到处理的方式有几种:1.让异步变成同步 2.使用GCD 3.使用信号量机制
写在结尾
目前成功的只有同步可以。
下一篇文章将会写到怎么iOS同步调用webservice服务。 并且探究下多线程知识。写错误的地方希望大家指正,毕竟还是有点菜的。能帮助到的话点个小星星哦。
网友评论