自定义 iOS soap 请求

提示文章只是用来记录本人自己在学习过程中所遇到的一些问题的解决方案,如果有什么意见可以留言提出来,不喜勿喷哦

自定义 iOS soap 请求_第1张图片
占位图
什么是 SOAP 协议

SOAP 是基于 XML 的简易协议,可使应用程序在 HTTP 之上进行信息交换。或者更简单地说:SOAP 是用于访问网络服务的协议。如果不了解的 SOAP 的话建议先去了解一下SOAP协议的结构。

问题提出

在我接手的一个关于汽车充电桩的项目里,所有的访问后台服务器都是使用的 SOAP 协议。所以,在最初的时候我采用的是 Git 上面的一个封装好的也是流传度很高的 SOAPEngine64(有兴趣的同学可以自己去了解了解)。
但是当我们需要对每次请求后台的 SOAP 请求进行登录用户和密码的验证的时候出现了问题:要求在 "" 里的 "" 里面加上自己的用户名和密码,以供验证。但 SOAPEngine64 里面似乎并没有给我们任何的接口或外部参数来添加这部分内容。没有办法的我只有自定义 SOAP 请求了。

方法构思

  • 自己创建一个 SOAP 请求的类,类里定义一个协议,添加方法用来实现请求的回调。
  • 创建一个方法用于外部直接调用 SOAP 请求,传入相应参数。
  • 接下来就是最关键的 SOAP 协议的拼接了,这里我把整个 SOAP 协议分为多段,最后再拼接在一起:
///////////soapMessage 拼接
    NSString *soapHtmlHeadH = [NSString stringWithFormat:
                              @""
                              ""
                              ""
                               ""];
    
    NSString *soapHtmlHeadF = [NSString stringWithFormat:
                              @""
                              ""
                              ""
                               ""
                              "<%@ xmlns=\"http://tempuri.org/\">"
                              "<%@>",self.soapMethodName,self.soapParamkey];
    NSString *soapHtmlFoot = [NSString stringWithFormat:
                              @""
                              ""
                             ""
                              "",self.soapParamkey,self.soapMethodName];
    
  
    NSString *soapMessage = [NSString stringWithFormat:@"%@%@%@%@%@",soapHtmlHeadH,headerParams,soapHtmlHeadF,params,soapHtmlFoot];
    
    NSLog(@"soapMessage: \n%@",soapMessage);

其中的就是我想添加进协议头部的参数名,headerParams就是具体的参数了。

  • 最后就是通过 http 请求发送请求了。但是注意的地方就是:接收到服务器的数据也是 xml 格式的,我们我们需要一个 XMLReader 类解析一下。

代码实现

XMLReader类

#import 

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 

+ (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
#import "XMLReader.h"

#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "XMLReader requires ARC support."
#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

#pragma mark - Public methods

+ (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

APISoapClient类

#import 

@protocol APISoapClientDelegate 

- (void)didReceiveData:(NSData *)data;

- (void)didFinishLoadingWithResult:(id)result isDictionary:(BOOL)isDictionary;

- (void)didFailWithError:(NSError *)error;

- (void)FailWithXML:(NSNumber *)code isDictionary:(BOOL)isDictionary errorDescription:(NSString *)errorDescription;
@end

typedef void (^ResultBlock) (id result,BOOL isDictionary,id message);

@interface APISoapClient : NSObject

{
    NSMutableData *_webData;
    
}

@property (nonatomic,copy) ResultBlock resultBlock;
@property (nonatomic,assign) NSInteger statusCode;

@property (nonatomic, copy) NSString *soapMethodName;
@property (nonatomic, copy) NSString *soapParamkey;

@property (nonatomic,assign) id delegate;

- (NSURLConnection *)getDataAPIResultWithURL:(NSString *)url
                                headerParams:(NSString *)headerParams
                                      params:(NSString *)params
                                 htttpMethod:(NSString *)htttpMethod//POST
                              withSOAPAction:(NSString *)soapAction
                              withMethodName:(NSString *)soapMethodName
                                withParamKey:(NSString *)soapParamkey
                             withResultBlock:(ResultBlock)resultBock;

@end
#import "APISoapClient.h"
#import "XMLReader.h"
#import "DictionaryWithJsonString.h"


@interface APISoapClient ()


@end

@implementation APISoapClient


- (NSURLConnection *)getDataAPIResultWithURL:(NSString *)url
                                headerParams:(NSString *)headerParams
                                      params:(NSString *)params
                                 htttpMethod:(NSString *)htttpMethod
                             withSOAPAction:(NSString *)soapAction
                             withMethodName:(NSString *)soapMethodName
                              withParamKey:(NSString *)soapParamkey
                                withResultBlock:(ResultBlock)resultBock
{
    self.soapMethodName = soapMethodName;
    self.soapParamkey = soapParamkey;
    self.resultBlock = resultBock;
    
///////////soapMessage 拼接
    NSString *soapHtmlHeadH = [NSString stringWithFormat:
                              @""
                              ""
                              ""
                               ""];
    
    NSString *soapHtmlHeadF = [NSString stringWithFormat:
                              @""
                              ""
                              ""
                               ""
                              "<%@ xmlns=\"http://tempuri.org/\">"
                              "<%@>",self.soapMethodName,self.soapParamkey];
    NSString *soapHtmlFoot = [NSString stringWithFormat:
                              @""
                              ""
                             ""
                              "",self.soapParamkey,self.soapMethodName];
    
  
    NSString *soapMessage = [NSString stringWithFormat:@"%@%@%@%@%@",soapHtmlHeadH,headerParams,soapHtmlHeadF,params,soapHtmlFoot];
    
    NSLog(@"soapMessage: \n%@",soapMessage);

    //请求发送到的路径
    NSURL *path = [NSURL URLWithString:url];
    NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:path cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:180.0];
    NSString *msgLength = [NSString stringWithFormat:@"%lu", (unsigned long)[soapMessage length]];
    [theRequest addValue:msgLength forHTTPHeaderField:@"Content-Length"];
    [theRequest addValue:@"text/xml;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
    
    [theRequest addValue:soapAction forHTTPHeaderField:@"SOAPAction"];
    [theRequest setHTTPMethod:htttpMethod];
    [theRequest setHTTPBody:[soapMessage dataUsingEncoding:NSUTF8StringEncoding]];
/////////////////
    //请求 iOS9.0后NSURLSession代替
    NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
    
    //如果连接已经建好,则初始化data
    if( theConnection )
    {
        _webData = [NSMutableData data];
    }
    else
    {
        NSLog(@"theConnection is NULL");
    }
    
    return theConnection;
}

/**
 *  Description  soap xml 拼接(Dic To xml)
 *
 *  @param soapHtmlMiddle soapHtmlMiddle description
 *  @param params         params description
 */
-(void)appendSoapHtmlMiddle:(NSMutableString **)soapHtmlMiddle WithParams:(NSMutableDictionary *)params
{
    for (NSString *key in params.allKeys) {
        
        [*soapHtmlMiddle appendString:[NSString stringWithFormat:@"<%@>",key]];
        
        id value = params[key];
        
        if ([value isKindOfClass:[NSDictionary class]]) {
            [self appendSoapHtmlMiddle:&*soapHtmlMiddle WithParams:value];
        }else if ([value isKindOfClass:[NSArray class]]){
            for (id oneValue in value) {
                if ([oneValue isKindOfClass:[NSDictionary class]]) {
                    [self appendSoapHtmlMiddle:&*soapHtmlMiddle WithParams:oneValue];
                }
            }
        }else{
            [*soapHtmlMiddle appendString:[NSString stringWithFormat:@"%@",value]];
        }
        
        [*soapHtmlMiddle appendString:[NSString stringWithFormat:@"",key]];
    }
}
/*
 */
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//    [webData setLength: 0];
    self.statusCode = [(NSHTTPURLResponse*)response statusCode];
//    NSLog(@"connection: didReceiveResponse:1------%@,statusCode:%ld",response,self.statusCode);

}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [_webData appendData:data];
   
        [self.delegate didReceiveData:data];
    
//    NSLog(@"connection: didReceiveData:2");
}

-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"ERROR with theConenction----:%@",error);
//    if (self.resultBlock) {
//        NSString *message = error.description;
//        self.resultBlock(NULL,NO,message);
//    }
    
    [self.delegate didFailWithError:error];
    
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//    NSLog(@"3 DONE. Received Bytes: %lu", (unsigned long)[_webData length]);
    
    NSString *st = [[NSString alloc] initWithData:_webData encoding:NSUTF8StringEncoding];
//    NSLog(@"connectionDidFinishLoading----:%@",st);

    //拼接key
    NSString * responseKey = [NSString stringWithFormat:@"%@"@"%@",_soapMethodName,@"Response"];
    NSString * resultKey = [NSString stringWithFormat:@"%@"@"%@", _soapMethodName, @"Result"];
    
    NSLog(@"得到的XML=%@", st);
    
    NSError *error;
     
    id resultDic = [XMLReader dictionaryForXMLString:st error:&error];
    
//    NSLog(@"result---:%@",resultDic);
    
    BOOL isDic = [resultDic isKindOfClass:[NSDictionary class]];
    id message;
    if (!error) {
        if (isDic) {
            resultDic = resultDic[@"soap:Envelope"][@"soap:Body"][responseKey][resultKey][@"text"];
            
            id resultDicTemp = [DictionaryWithJsonString dictionaryWithJsonString:resultDic];
            if(resultDicTemp == nil){//string 类型
                NSLog(@"Sting 类型数据无需转换字典类型");
                isDic = false;
            }else{                   //字典类型
                resultDic = resultDicTemp;
                message = resultDic[@"Value"];
            }
        }else{

            resultDic = [NSNumber numberWithInteger:110];
        }
         NSLog(@"%@ request end",self.soapMethodName);
        
//        if (self.resultBlock) {
//            self.resultBlock(resultDic,isDic,message);
//        }
        if([_delegate respondsToSelector:@selector(didFinishLoadingWithResult:isDictionary:)]){
            [_delegate didFinishLoadingWithResult:resultDic isDictionary:isDic];
        }else{
            NSLog(@"NoSuchMethod");
        }
        
       
    }else {
        NSLog(@"解析错误%@",error);
//        if (self.resultBlock) {
//            self.resultBlock([NSNumber numberWithInteger:110],isDic,error.description);
//        }
        
            [self.delegate FailWithXML:[NSNumber numberWithInteger:110] isDictionary:isDic errorDescription:error.description];
        
    };
    
}


@end

注意:在解析xml的过程中需要根据自己的设计情况提取 XML 参数!

你可能感兴趣的:(自定义 iOS soap 请求)