WSDL是什么呢?Web Services Description Language的缩写,是一个用来描述Web服务和说明如何与Web服务通信的XML语言。WSDL 文件包含以下元素:

Type:使用某种语法(如 XML 模式)的数据类型定义(string、int) Message:要传递的数据 Part:消息参数 Operation:服务支持的操作的抽象描述 Port Type / Interface:一个或多个端点支持的操作的抽象集。此名称已更改,因此可能会遇到两者中的任何一个。 Binding:特定端口类型的具体协议和数据格式规范 Port / Endpoint:绑定和网络地址的组合。此名称也已更改,因此可能会遇到两者中的任何一个。 Service:相关端点的集合,包括其关联的接口、操作、消息等。

给我们看了API的声明和发送以及返回的XML格式后,我们还需要看下WSDL文件,才能随心所欲地针对各种OpenAPI书写代码。提供的接受我们发送的数据的地址为:,查看其WSDL声明的URL就是:?WSDL。一开始看是不是很头大?我们将其进行分块。

一个WSDL 的文档的主要结构是类似这样的:

 

<definitions>
    <types>
    <!--  definition of types........-->
      
  </types>
    <message>
    <!--  definition of a message....-->
      
  </message>
    <portType>
    <!--  definition of a port.......-->
      
  </portType>
    <binding>
    <!--  definition of a binding....-->
      
  </binding>

这下就清楚了吧?具体的WSDL声明请见:http://www.w3.org/TR/wsdl。当然我们仅仅做调用开发的话,并不需要知道得那么详细。我们首先能看到的OpenAPI的WSDL中开头有定义了很多名称空间,(NameSpace):

<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
                  xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" 
                  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
                  xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" 
                  xmlns:tns="" 
                  xmlns:s1="http://microsoft.com/wsdl/types/" 
                  xmlns:s="http://www.w3.org/2001/XMLSchema" 
                  xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" 
                  xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" 
                  targetNamespace="" 
                  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">

这些东西我们是需要的,看到了其中的s和s1了吧?我们的数据类型可定义在里面呢,这些对我们来说是很重要的,虽然在一篇中我们并没有用到,因为类型都是string,个人理解是因为默认的缘故,所以不需要声明节点的数据类型,不知道个人理解准确与否,不当之处还请读者斧正。<wsdl:portType name="ForumAPISoap">元素中,定义了各个API的发送和接受的Message。

<wsdl:portType name="ForumAPISoap">
  <wsdl:operation name="Post">
    <wsdl:input message="tns:PostSoapIn" />
    <wsdl:output message="tns:PostSoapOut" />
  </wsdl:operation>
  <wsdl:operation name="Reply">
    <wsdl:input message="tns:ReplySoapIn" />
    <wsdl:output message="tns:ReplySoapOut" />
  </wsdl:operation>
  <wsdl:operation name="GetForums">
    <wsdl:input message="tns:GetForumsSoapIn" />
    <wsdl:output message="tns:GetForumsSoapOut" />
  </wsdl:operation>
  <wsdl:operation name="CheckOutTopic">
    <wsdl:input message="tns:CheckOutTopicSoapIn" />
    <wsdl:output message="tns:CheckOutTopicSoapOut" />
  </wsdl:operation>
  <wsdl:operation name="GetTopicsOfUser">
    <wsdl:input message="tns:GetTopicsOfUserSoapIn" />
    <wsdl:output message="tns:GetTopicsOfUserSoapOut" />
  </wsdl:operation>
  <wsdl:operation name="PointDonate">
    <wsdl:input message="tns:PointDonateSoapIn" />
    <wsdl:output message="tns:PointDonateSoapOut" />
  </wsdl:operation>
  <wsdl:operation name="GetUserProfile">
    <wsdl:input message="tns:GetUserProfileSoapIn" />
    <wsdl:output message="tns:GetUserProfileSoapOut" />
  </wsdl:operation>
</wsdl:portType>

 

我们来看的另外一个OpenAPI,这个是很多人都很关心的发帖的API——Post。从上面的portType中,我们可以看到input message是tns:PostSoapIn,output message是tns:PostSoapOut。各个Message的定义在上面,

<wsdl:message name="PostSoapIn">
  <wsdl:part name="parameters" element="tns:Post" />
</wsdl:message>
<wsdl:message name="PostSoapOut">
  <wsdl:part name="parameters" element="tns:PostResponse" />
</wsdl:message>

通过这里,我们知道了: PostSoapIn的Element是Post,而PostSoapOut的Element是PostResponse。我们还得往上看,我们知道了input和output的message,还需要在上面查message对应的Element声明。在Type中,我们看到了声明

<s:complexType name="Post">
  <s:sequence>
    <s:element minOccurs="1" maxOccurs="1" name="forumId" type="s1:guid" />
    <s:element minOccurs="0" maxOccurs="1" name="subject" type="s:string" />
    <s:element minOccurs="0" maxOccurs="1" name="body" type="s:string" />
    <s:element minOccurs="0" maxOccurs="1" name="tag" type="s:string" />
    <s:element minOccurs="1" maxOccurs="1" name="point" type="s:int" />
    <s:element minOccurs="1" maxOccurs="1" name="isAskExpert" type="s:boolean" />
    <s:element minOccurs="0" maxOccurs="1" name="expertUserName" type="s:string" />
    <s:element minOccurs="1" maxOccurs="1" name="editor" type="tns:EditorType" />
    <s:element minOccurs="0" maxOccurs="1" name="url" type="s:string" />
  </s:sequence>
</s:complexType>
 
<s:element name="PostResponse">
  <s:complexType>
    <s:sequence>
      <s:element minOccurs="1" maxOccurs="1" name="PostResult" type="s:boolean" />
      <s:element minOccurs="1" maxOccurs="1" name="error" type="tns:Error" />
      <s:element minOccurs="0" maxOccurs="1" name="topicUrl" type="s:string" />
    </s:sequence>
  </s:complexType>
</s:element>
<s:complexType name="Error">
  <s:sequence>
    <s:element minOccurs="1" maxOccurs="1" name="errId" type="s:int" />
    <s:element minOccurs="0" maxOccurs="1" name="errInfo" type="s:string" />
    <s:element minOccurs="0" maxOccurs="1" name="description" type="s:string" />
  </s:sequence>
</s:complexType>

而同时,在这之前我们就知道,Post这个API的发送的XML格式定义是:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
               xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <Post xmlns="">
      <identity>
        <username>string</username>
        <password>string</password>
      </identity>
      <post>
        <forumId>guid</forumId>
        <subject>string</subject>
        <body>string</body>
        <tag>string</tag>
        <point>int</point>
        <isAskExpert>boolean</isAskExpert>
        <expertUserName>string</expertUserName>
        <editor>Text or Html or UBB</editor>
        <url>string</url>
      </post>
    </Post>
  </soap:Body>
</soap:Envelope>
返回的XML格式定义:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
               xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <PostResponse xmlns="">
      <PostResult>boolean</PostResult>
      <error>
        <errId>int</errId>
        <errInfo>string</errInfo>
        <description>string</description>
      </error>
      <topicUrl>string</topicUrl>
    </PostResponse>
  </soap:Body>
</soap:Envelope>

我们可以看到,他们其实就是WDSL声明中的一个转换罢了,换了种直观的说法。如果我们是采用C#调用OpenAPI的话,就完全不需要考虑跑到那个WDSL文件里面去研究message,Type什么的,直接写在结构体里面,定义好就发送结构体,.Net内部都会自动帮你进行转换,但是C++就麻烦多了。^_^

我们需要发送的数据中有GUID类型,int类型,甚至boolean类型,这在C++中如何表示给XML知道呢?我们需要手工为Element打上类型标记,先看forumId这个元素,是guid类型,在WDSL中我们看到<s:element minOccurs="1" maxOccurs="1" name="forumId" type="s1:guid" />这么一行,说明它的类型是s1:guid,同理,int对应的表示为s:int,我们的代码需要进行改动。

还记得第一篇中的:

 

Serializer->startEnvelope("SOAP","http://schemas.xmlsoap.org/soap/envelope/","");
 
吗,呵呵,我们给它加上三行名称空间定义。
       Serializer->SoapAttribute("tns","","","xmlns");
       Serializer->SoapAttribute("s","","http://www.w3.org/2001/XMLSchema","xmlns");
       Serializer->SoapAttribute("s1","","http://microsoft.com/wsdl/types/","xmlns");

 

现在就简单了,给Element开始的时候打上类型标记即可:

 

Serializer->startBody("");  //<soap:Body>
#pragmaregion
       Serializer->startElement("Post","","NONE",""); //<Post xmlns="">
#pragmaregion
              Serializer->startElement("identity","","NONE","");//<identity>
 
                     Serializer->startElement("username","","NONE",""); 
                     Serializer->SoapAttribute("type", "", "s:string", "");
                     Serializer->writeString("tr0j4n"); 
                     Serializer->endElement(); 
 
                     Serializer->startElement("password","","NONE",""); 
                     Serializer->SoapAttribute("type", "", "s:string", "");
                     Serializer->writeString("5eab01aa1a2df9a5"); 
                     Serializer->endElement();
 
              Serializer->endElement(); //</identity>
#pragmaendregion
 
#pragmaregion
              Serializer->startElement("post","","NONE","");//<post>
#pragmaregion
                     Serializer->startElement("forumId","","NONE",""); 
                     Serializer->SoapAttribute("type", "", "s1:guid", "");
                     Serializer->writeString("b3f8b246-f953-40a4-a4d4-7b8546ecce6c"); 
                     Serializer->endElement(); 
#pragmaendregion
#pragmaregion
                     Serializer->startElement("subject","","NONE",""); 
                     Serializer->SoapAttribute("type", "", "s:string", "");
                     Serializer->writeString("PostThreadTest"); 
                     Serializer->endElement(); 
#pragmaendregion
#pragmaregion
                     Serializer->startElement("body","","NONE",""); 
                     Serializer->SoapAttribute("type", "", "s:string", "");
                     Serializer->writeString("RT"); 
                     Serializer->endElement(); 
#pragmaendregion
#pragmaregion
                     Serializer->startElement("tag","","NONE",""); 
                     Serializer->SoapAttribute("type", "", "s:string", "");
                     Serializer->writeString(""); 
                     Serializer->endElement(); 
#pragmaendregion
#pragmaregion
                     Serializer->startElement("point","","NONE",""); 
                     Serializer->SoapAttribute("type", "", "s:int", "");
                     //int x=100;
                     Serializer->writeString("100"); 
                     Serializer->endElement(); 
#pragmaendregion
#pragmaregion
                     Serializer->startElement("isAskExpert","","NONE",""); 
                     Serializer->SoapAttribute("type", "", "s:boolean", "");
                     //bool y=true;
                     Serializer->writeString("1"); 
                     Serializer->endElement(); 
#pragmaendregion
#pragmaregion
                     Serializer->startElement("expertUserName","","NONE",""); 
                     Serializer->SoapAttribute("type", "", "s:string", "");
                     Serializer->writeString("ALL"); 
                     Serializer->endElement(); 
#pragmaendregion
#pragmaregion
                     Serializer->startElement("editor","","NONE",""); 
                     Serializer->SoapAttribute("type", "", "tns:EditorType", "");
                     Serializer->writeString("UBB"); 
                     Serializer->endElement(); 
#pragmaendregion
 
              Serializer->endElement(); //</post>
#pragmaendregion
       Serializer->endElement();//</Post>
#pragmaendregion
       Serializer->endBody();//</soap:Body>

 

如上就是一个完整的发帖的C++调用Soap的代码了,b3f8b246-f953-40a4-a4d4-7b8546ecce6c是小版块的GUID,以下的分别是帖子的标题、帖子正文内容、标签,分数、是否向专家提问、专家姓名、编辑器种类(目前只支持UBB)。你全用WriteString即可,因为XML本来就是纯文本,那些类型的标示是在服务器接收到之后用作转换只用,这些资料网上都搜索不到,tr0j4n当时探索这个花了很多时间,走了不少弯路,现在吐血把心得都发出来给大家了。细心的读者发现其实少了一个字段,url,这个是帖子的URL,不过我们还没有发帖,怎么知道URL呢?没事,不写就好了,能这里考虑得有些问题。服务器返回的XML中不是有个topicUrl嘛?那个就是生成的帖子的URL。