SOAP协议和基于SOAP的开发流程详述

【文章摘要】
简单对象访问协议(SOAP)是交换数据的一种协议规范,是一种轻量的、简单的、基于XML的协议,它被设计成在WEB上交换结构化的和固化的信息。
在实际的软件开发项目中,SOAP大多用于处理用户数据,也即实现开户、销户、改户和查户等功能。
本文根据作者实际从事过的基于SOAP的项目开发,对SOAP协议和基于SOAP的开发流程进行了详细的介绍。希望大家通过此文,能够对基于SOAP协议的开发有一个全面的了解。

一、soapUI和SOAP代码生成框架简介
“工欲善其事,必先利其器”,为了基于SOAP协议进行开发,我们需要事先准备好相关的工具软件。在我最开始进行开发的时候,并没有考虑到工具的重要性,等代码写好之后才发现没有自测的工具,这个时候才匆忙地找同事要相关的软件来安装。这样做影响了工作的效率。因此,“兵马未动,粮草先行”,代码未写,工具要先装好。
下面来介绍与SOAP相关的两个软件:soapUI和SOAP代码生成框架。
1.soapUI
soapUI是一个基于SOAP的模拟测试工具,用于模拟WEB客户端向SOAP消息处理模块(我们要开发的模块)发消息。我们可以用该软件来对自己所编写的程序进行单元和集成测试。
图1是soapUI的软件界面。
SOAP协议和基于SOAP的开发流程详述_第1张图片
图1 soapUI的软件界面

2.SOAP代码生成框架
在基于SOAP进行开发的时候,我们会用到很多SOAP自带的、已经封装好了的函数。为了避免重复劳动,SOAP的设计者为我们提供了一套叫做gsoap的自动生成代码的框架。只要我们将wsdl文件(后面介绍)准备好,用两个简单的命名即可生成我们在开发过程中需要用到的相关函数文件。
不管大家在哪个平台(Windows、Linux或Mac OS)下开发程序,都可以利用此框架来生成代码。自动生成代码的工具布局如图2所示。
SOAP协议和基于SOAP的开发流程详述_第2张图片
图2 自动生成代码的工具布局
我们将相应的wsdl文件放入对应的文件夹中即可生成代码框架。

二、基于SOAP开发的软件模块架构
一般说来,发送SOAP消息的都是与用户直接打交道的外部模块,这些消息需要转变为内部消息之后,才能够用于执行具体的处理。因此,基于SOAP开发的软件模块架构如图3所示。
这里写图片描述
图3 基于SOAP开发的软件模块架构
从图3可以看出,系统一般不会直接用SOAP消息来执行具体的业务逻辑,而是用一个中间模块将SOAP消息转换为系统内部约定好的协议,然后由具体的业务执行模块按照内部协议来执行具体的操作。
这样做的好处有:
第一,将消息转换与业务执行分离开来,便于程序实现和问题排查。
第二,不将业务的具体执行流程暴露出去,系统的安全性能更高。
第三,如果外部的SOAP协议经常变化,只需要修改中间的SOAP协议转换模块即可,不用让具体业务处理模块经常变动而影响了正常业务的执行。

三、基于SOAP的具体开发流程
准备工作都做好之后,我们接下来就要动手编写程序了。下面以实现用户数据的管理(即:增删改查)为例,介绍整个开发的流程。
1.编写wsdl文件
wsdl文件,也即是SOAP协议文件,用于约定要执行哪些具体操作。例如,我们要在系统中执行开户操作,需要携带的字段包括用户的电话号码、用户名和密码。那么,我们就可以编写如下的命名为“Isoapboss.wsdl”的文件:


    <wsdl:definitions targetNamespace="soap" xmlns:tns="soap" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://mail.dto.op.web.ngmail.zte.com" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenc11="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soapenc12="http://www.w3.org/2003/05/soap-encoding" xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
        <wsdl:types>
            <xsd:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="soap" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
                <xsd:element name="CreateUserReqt">
                    <xsd:complexType>
                        <xsd:sequence>
                            <xsd:element maxOccurs="1" minOccurs="1" name="UserPhone"        nillable="true" type="xsd:string"/>
                            <xsd:element maxOccurs="1" minOccurs="0" name="UserName"         nillable="true" type="xsd:string"/>
                            <xsd:element maxOccurs="1" minOccurs="0" name="Password"         nillable="true" type="xsd:string"/>            
                        xsd:sequence>
                    xsd:complexType>
                xsd:element>
                <xsd:element name="CreateUserResponse">
                    <xsd:complexType>
                        <xsd:sequence>
                            <xsd:element maxOccurs="1" minOccurs="1" name="resultcode"              type="xsd:int"/>
                            <xsd:element maxOccurs="1" minOccurs="1" name="description"             type="xsd:string"/>                 
                        xsd:sequence>
                    xsd:complexType>
                xsd:element>
            xsd:schema>
        wsdl:types>

        <wsdl:message name="CreateUserRequest">
            <wsdl:part name="parameters" element="tns:CreateUserReqt"/>
        wsdl:message>

        <wsdl:message name="CreateUserResponse">
            <wsdl:part name="parameters" element="tns:CreateUserResponse"/>
        wsdl:message> 

        <wsdl:portType name="IsoapbossPortType">
          <wsdl:operation name="CreateUser">
            <wsdl:input name="CreateUserRequest" message="tns:CreateUserRequest"/>
            <wsdl:output name="CreateUserResponse" message="tns:CreateUserResponse"/>
          wsdl:operation>
        wsdl:portType>

        <wsdl:binding name="IsoapbossHttpBinding" type="tns:IsoapbossPortType">
          <wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
          <wsdl:operation name="CreateUser">
            <wsdlsoap:operation soapAction=""/>
            <wsdl:input name="CreateUserRequest">
              <wsdlsoap:body use="literal"/>
            wsdl:input>
            <wsdl:output name="CreateUserResponse">
              <wsdlsoap:body use="literal"/>
            wsdl:output>
          wsdl:operation>
        wsdl:binding>

        <wsdl:service name="Isoapboss">
          <wsdl:port name="IsoapbossHttpPort" binding="tns:IsoapbossHttpBinding">
            <wsdlsoap:address location="http://10.10.10.10:9999/soapboss/Isoapboss"/>
          wsdl:port>
        wsdl:service>
    wsdl:definitions> 

对于以上文件内容,解释如下:
第一,开户时需要包括的消息字段包括UserPhone、UserName和Password,其中UserPhone为必填字段(maxOccurs和minOccurs的值均为1),UserName和Password为选填字段。响应消息包括resultcode(结果码)和description(结果描述)字段。
第二,文件最后的“http://10.10.10.10:9999/soapboss/Isoapboss”中的“10.10.10.10”和“9999”表示图3中的SOAP协议转换模块所在机器的IP地址和该模块所绑定的端口号。这两个值可以在SOAP协议转换模块的配置文件中填写。

2.利用wsdl文件生成SOAP代码框架
因为我们是在Windows下面编写的程序,所以将上一步写好的“Isoapboss.wsdl”文件放到图2中的“win32”文件夹下,如图4所示。
SOAP协议和基于SOAP的开发流程详述_第3张图片
图4 wsdl文件放置位置示意图
从图4中可以看到,除了“Isoapboss.wsdl”文件之外,该文件夹下还有“soapcpp2.exe”和“wsdl2h.exe”两个文件,这两个文件用于生成代码框架。
在cmd窗口中依次执行“wsdl2h.exe -c Isoapboss.wsdl”和“soapcpp2.exe Isoapboss.h”命令,如图5所示。
SOAP协议和基于SOAP的开发流程详述_第4张图片
图5 代码框架的生成命令执行示意图
执行命令之后,便在“win32”文件夹下生成了相关的代码文件,如图6所示。
SOAP协议和基于SOAP的开发流程详述_第5张图片
图6 代码文件生成之后的文件存放示意图
我们只需要将图6中的“.c”和“.h”文件拷贝到我们的代码工程中就OK了。

3.编写具体的SOAP消息处理代码
图3中的SOAP协议转换模块用于接收、解析SOAP消息,然后转换成内部约定好的消息结构。
该模块的主要功能有这三个:接收SOAP消息、处理SOAP消息、解析SOAP消息并进行组装。下面分别来进行说明。
(1)接收SOAP消息
该处理流程的伪代码如下:

soap_init(&soap);    // 初始化soap
m = soap_bind(&soap, IpAdr, Port, BACKLOG);   // 绑定IP和端口号

while (!soap_valid_socket(m))   // 循环直至服务套接字合法
{
    m = soap_bind(&soap, IpAdr, Port, BACKLOG);
}
……
for (;;)      // 用一个死循环来不停地接收SOAP消息
{
    s = soap_accept(&soap);     // 接收到SOAP消息
    if (!soap_valid_socket(s))    // 对错误消息的处理
    { 
        ……
    } 
    // 将接收到的SOAP消息放入队列供处理线程处理
    EnQueue(data);   
}

(2)处理SOAP消息
该处理流程的伪代码如下:

for (;;)    // 用一个死循环来不停地处理SOAP消息
{ 
    DeQueue(&data);  // 将SOAP消息从队列中取出来处理
    ……
    soap_set_namespaces(tsoap, namespaces);  // 设置命名空间
    soap_serve(tsoap);     // 具体处理SOAP消息的函数
    soap_destroy(tsoap);   // 处理完成之后,销毁该SOAP消息
    soap_end(tsoap);      // 结束本次处理
}

(3)解析SOAP消息并进行组装
这一步需要我们实现soapStub.h文件中的__ns1__CreateUser函数。
这个流程因为具体的业务不同而不同,在此不再具体说明。

以上三个流程中用的函数均为我们利用wsdl文件生成的SOAP代码框架中的已经封装好了的函数。

4.对编写的代码进行测试
按照惯例,代码编写好之后,我们便要对之进行严格的测试。
对于SOAP消息的测试,我们第一步要做的便是在soapUI软件中新建工程,并将wsdl文件导入进去。例如,我们建立一个名叫TestSoap的工程,如图7所示。
SOAP协议和基于SOAP的开发流程详述_第6张图片
图7 新建TestSoap工程示意图
我们为UserPhone、UserName和Password填上具体的值,然后单击发消息的按钮,便开始了正式的对整个程序的测试。测试执行之后的软件界面如图8所示。
SOAP协议和基于SOAP的开发流程详述_第7张图片
图8 测试执行之后的软件界面
从图8中我们可以看到,开户流程执行成功。
为了对程序进行全面的测试,可以设置多组测试用例。

以上便是基于SOAP协议的总体开发流程,具体的编程细节取决于大家所参与的项目的实际情况,但基本的流程都是一致的。

四、总结
本文基于作者的实际开发经历,对SOAP协议和基于SOAP的开发流程进行了详细的说明,供从事相关开发的朋友们参考。
在整个开发的过程中,我们要注意以下几点:
第一,wsdl文件的编写至关重要,这相当于是内部系统与用户之间的协议。在编写代码之前,大家一定要将协议弄清楚,以免后期修改所带来的麻烦。
第二,在对代码进行测试的时候,大家一定要详细阅读程序所产生的日志和数据库中的相关数据,确保每个处理流程的执行都是正确无误的。
第三,一定要与用户约定好执行之后的相关结果码(即resultcode的值),哪些值表示执行成功,哪些值表示执行失败,都要考虑好。这样也方便查找程序问题。
第四,对于不同的soapUI的版本,对未填写的字段的处理会有所不同。有些版本的soapUI会将未填写的字段当成空指针来处理,大家在编写代码的时候要特别注意。


欢迎大家关注并支持本人新书《C程序员从校园到职场》。

你可能感兴趣的:(编程)