【摘要】
本文主要对SOAP(Simple Object AccessProtocol)协议的相关知识进行介绍,并说明如何调用使用SOAP作为传输协议的接口。
【正文】
SOAP是一种轻型的,基于XML的数据交换协议,它在底层仍然使用HTTP协议传输,但是与HTTP的请求包不同,HTTP传递的是HTTP文本,SOAP传递的是结构化的数据。虽然如今已经是HTTP和JSON的天下,使用SOAP协议的Web Service也有些过时了,但是由于其安全性和底层可扩展的特性,大量的国企、政府的系统仍然在使用。为了顺利地与他们的接口对接,还是需要了解一下相关的技术。
SOAP协议基于XML语言,在学习SOAP协议之前需要了解XML的相关知识。XML是一种可扩展的标记语言,类似于HTML,但是与HTML的用途不一样。HTML用于页面的展示,其标签是预先被设计好的,用户根据标签的功能选择使用,以便设计出预期的页面。XML是为了数据的传输而设计的,其标签没有被预定义,使用者需要自己定义标签。可以认为XML就是一种纯文本,没有任何其他的功能。
在XML中,由于标签都是开发者自己定义的,如果两份xml文档出现含义不同的相同名称的标签,将会发生命名冲突,导致XML解析器无法解析,例如:
1 2 3 4 |
<apple> <color>red</color> <producer>ShanXi</producer> </apple> |
1 2 3 4 |
<apple> <business>electronics</business> <address>California</address> </apple> |
以上两个不同内容与含义的<apple>标签如果一起使用,就会发生命名冲突,解决方法有两种,一种是添加前缀,表明前一种apple是水果(fruit),后一种apple是一家公司。:
1 2 3 4 |
<f:apple> <f:color>red</f:color> <f:producer>ShanXi</f:producer> </f:apple> |
1 2 3 4 |
<c:apple> <c:business>electronics</c:business> <c:address>California</c:address> </c:apple> |
还有一种方式是使用命名空间,即为标签添加一个xmlns属性,指定命名空间后,所有带有相同前缀的子元素都会与该命名空间相关联。命名空间的语法为:
xmlns:namespace-prefix="namespaceURI"
1 2 3 4 |
<c:apple xmlns:c="http://www.apple.com"> <c:business>electronics</c:business> <c:address>California</c:address> </c:apple> |
通常,使用默认的命名空间可以省去前缀:
1 2 3 4 |
<apple xmlns="http://www.apple.com"> <business>electronics</business> <address>California</address> </apple> |
命名空间的值一般会使用统一资源标识符,不过很多公司会使用一个真实的URL来指向实际存在的,包含公司信息的网页。需要注意的是,用于标识命名空间的地址不会真的被解析器访问,它的唯一作用就是确定一个唯一的名称。
由于SOAP是一种基于XML的通信协议,一条SOAP消息本质上就是一个XML文本,但是它必须含有 Envelope 元素,以便与普通的XML文本区分,即SOAP信息的根元素必须为<soap:Envelope></soap:Envelope>。此外,请求体<soap:Body></soap:Body>元素也是必须的。除了上述两个元素,一条SOAP消息还可以提供包含头部信息的 Header元素和用于处理错误的Fault元素。因此,一条正确的SOAP消息的结构看起来如下:
1 2 3 4 5 6 7 8 9 10 |
<?xml version="1.0"?> <soap:Envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope" soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding"> <soap:Header>头部信息</soap:Header> <soap:Body>请求体</soap:Body> </soap:Envelope> |
下面解释一下各个元素的用法:
1、Envelope元素
前面说过,SOAP语法规定一条SOAP消息必须拥有一个Envelope根元素,而这个根元素又必须与固定的命名空间相关联,在SOAP1.1中,这个命名空间是http://schemas.xmlsoap.org/soap/envelope,在SOAP1.2中,这个命名空间是http://www.w3.org/2003/05/soap-envelope。如果使用了不同的命名空间,程序将无法识别该消息。因此,SOAP消息的Envelope标签必须有如下属性:
xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
此外还可以指定encodingStyle
属性来定义文档使用的数据类型,但这个属性并非一定要在定义在Envelope标签中,它可以出现在任何标签中,影响范围为该标签和其子标签的内容。
2、Body元素
Body元素也是必须要书写的,它包含实际要传输的内容。在Body元素内部我们可以根据服务端的接口文档定义要请求的方法和传递的参数,具体见后文关于WSDL的介绍。
3、Header元素
首先,Header元素必须是Encelope元素的第一个子元素,其次,Header元素的子元素必须是一个命名空间。Header元素多数情况下用于传递用户的身份验证信息,例如将token放入其中。
WSDL全称Web Services
Description Language,即web service的描述语言,用于描述web service定义的方法和属性等。WSDL文档的根元素为<definitions></definitions>,并由以下子元素组成:
·
types:定义数据类型
·
message:定义将被传输的数据
·
portType:设置web service将要执行的操作
·
binding:指定web service的通信协议
在这里我们不详细讨论其语法,主要说一下如何快速找到接口相关的信息。下面通过一个免费的webservice接口具体分析一下用法,接口地址是http://www.webxml.com.cn/webservices/qqOnlineWebService.asmx?wsdl
这个接口可以通过输入QQ号查看该QQ号的在线状态。GET请求该接口地址可以获得该地址的WSDL文档。
首先,最重要的部分是portType元素,name属性给出其名称。该元素下会有<operation></operation>元素,name属性给出操作名称。operation的子元素为input和output,定义输入和输出消息,它们会有一个message属性,指向对应name属性的message元素。可以认为portType定义了一个类(模块),operation是它的函数,带有输入参数input和输出参数output。
1 2 3 4 5 6 7 |
<wsdl:portType name="qqOnlineWebServiceHttpPost"> <wsdl:operation name="qqCheckOnline"> <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"><br /><h3>获得腾讯QQ在线状态</h3><p>输入参数:QQ号码 String,默认QQ号码:8698053。返回数据:String,Y = 在线;N = 离线;E = QQ号码错误;A = 商业用户验证失败;V = 免费用户超过数量</p><br /></wsdl:documentation> <wsdl:input message="tns:qqCheckOnlineHttpPostIn"/> <wsdl:output message="tns:qqCheckOnlineHttpPostOut"/> </wsdl:operation> </wsdl:portType> |
message元素本身有一个name属性,与input/output相关联,它的子元素为part,定义了参数名和参数类型
1 2 3 4 5 6 |
<wsdl:message name="qqCheckOnlineHttpPostIn"> <wsdl:part name="qqCode" type="s:string"/> </wsdl:message> <wsdl:message name="qqCheckOnlineHttpPostOut"> <wsdl:part name="Body" element="tns:string"/> </wsdl:message> |
文档中还有一点需要注意,根元素指定了targetNamespace命名空间,这说明WSDL文档声明的所有元素默认属于该命名空间,具体见XML Schema的内容,这里不再赘述。XML Schema为XML文档提供校验,因此SOAP请求的BODY元素的命名空间需要和WSDL文档中targetNamespace命名空间保持一致。
1 |
<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="http://WebXml.com.cn/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
targetNamespace="http://WebXml.com.cn/"> |
通过观察文档构造如下请求:
1 2 3 4 5 6 7 8 |
<?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://www.w3.org/2003/05/soap-envelope"> <soap:Body> <qqCheckOnline xmlns="http://WebXml.com.cn/"> <qqCode>12345678</qqCode> </qqCheckOnline> </soap:Body> </soap:Envelope> |
返回信息如下:
1 2 3 4 5 6 7 8 |
<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <qqCheckOnlineResponse xmlns="http://WebXml.com.cn/"> <qqCheckOnlineResult>Y</qqCheckOnlineResult> </qqCheckOnlineResponse> </soap:Body> </soap:Envelope> |
读者可自行验证。
在查找资料的过程中可以发现,web service的知识总是伴随着java、.net等关键字,部分理论对于非相关开发者来说并不容易理解。对于只关心接口调用的读者来说,前面几章内容均可跳过,因为现在有了一个高度封装的可以用于解析SOAP的python包——Zeep。
Zeep包使用非常简单,直接import即可,以下是对第四章查看QQ是否在线的接口的请求:
1 2 3 4 5 |
import zeep wsdl = "http://www.webxml.com.cn/webservices/qqOnlineWebService.asmx?wsdl" client = zeep.Client(wsdl=wsdl) client.service.qqCheckOnline(qqCode=12345678) |