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。