一、SOAP协议简介
1、SOAP简介
SOAP(Simple Object Accrss Protocol,简单对象访问协议)是一种简单的基于XML的协议,可以使应用程序在分散或分布式的环境中通过HTTP来交换信息。
SOAP基于XML语言和XSD标准,其定义了一套编码规则,编码规则定义如何将数据表示为消息,以及怎样通过HTTP协议来传输SOAP消息,由四部分组成:
(1) SOAP信封(Envelope):定义了一个框架,框架描述了消息中的内容是什么,包括消息的内容、发送者、接收者、处理者以及如何处理消息。
(2)SOAP编码规则:定义了一种系列化机制,用于交换应用程序所定义的数据类型的实例。
(3) SOAP RPC表示:定义了用于表示远程过程调用和应答协定。
(4)SOAP绑定:定义了一种使用底层传输协议来完成在节点间交换SOAP信封的约定。
SOAP消息基本上是从发送端到接收端的单向传输,常常结合起来执行类似于请求/应答的模式。不需要把SOAP消息绑定到特定的协议,SOAP可以运行在任何其他传输协议(HTTP、SMTP、FTP等)上。另外,SOAP提供了标准的RPC方法来调用Web Service以请求/响应模式运行。
SOAP是Web Service的通信协议。当用户通过UDDI找到你的WSDL描述文档后,他通过可以SOAP调用你建立的Web服务中的一个或多个操作。SOAP是XML文档形式的调用方法的规范,可以支持不同的底层接口,像HTTP(S)或者SMTP。
应用程序通过使用远程过程调用(RPC)在诸如 DCOM 与 CORBA 等对象之间进行通信的方式会产生兼容性以及安全问题;防火墙和代理服务器通常会阻止此类流量。通过 HTTP 在应用程序间通信是更好的方法,因为 HTTP 得到了所有的因特网浏览器及服务器的支持。SOAP 提供了一种标准的方法,使得运行在不同的操作系统并使用不同的技术和编程语言的应用程序可以互相进行通信。
2、SOAP特性
-
SOAP是一种轻量级通信协议
-
SOAP用于应用程序之间的通信
-
使用SOAP的应用使用HTTP协议通信
-
SOAP独立于平台
-
SOAP独立于编程语言
-
SOAP基于XML
-
SOAP很简单并可扩展
-
SOAP允许绕过防火墙
二、SOAP 消息交换模型
1、SOAP结点
SOAP结点表示SOAP消息路径的逻辑实体,用于进行消息路由或处理。SOAP结点可以是SOAP消息的发送者、接收方、消息中介。
在SOAP消息模型中,中间方为一种SOAP结点,负责提供发送消息的应用程序和接收方间的消息交换和协议路由功能。中间方结点驻留在发送结点和接收结点之间,负责处理SOAP消息头中定义的部分消息。SOAP发送方和接收方之间可以有0个或多个SOAP中间方,为SOAP接收方提供分布式处理机制。
一般,SOAP消息中间方分为两种:
A、转发中间方:转发中间方通过在所转发消息的SOAP消息头块中描述和构造语义和规则,从而实现消息处理。
B、活动中间方:活动中间方利用一组功能为接收方结点修改外部绑定消息,从而提供更多的消息处理操作。
在SOAP消息交换路径中,借助于SOAP中间方,使得分布式处理模型在SOAP消息交换中得以实现。通过使用SOAP中间方,可以向SOAP应用程序中集成各种功能(如转发、过滤、事务、安全、日志记录、智能路由等)。
2、无消息提供者的客户端
不使用消息提供者的应用程序只能交换同步消息,即扮演客户端角色的应用程序只能发送请求-响应消息。客户端采用SAAJ API的SOAPConnection方法。下图演示了在没有消息提供者的情况下,同步消息如何在发送者和接收者之间交换。
不使用消息提供者的客户端具有优点如下:
A、可以采用J2SE平台编写应用程序。
B、不需要在servlet或J2EE容器中部署应用程序。
C、不需要配置消息提供者。
不使用消息提供者的客户端的局限性如下:
A、客户端只能发送请求-响应消息
B、 客户端只能扮演客户端角色
3、使用消息提供者的客户端
如果想要获得并且保存在任何时间发送给你的请求,必须使用消息提供者。使用消息提供者的客户端还能发送异步消息。JAXM API提供了使用消息提供者发送和接收消息的框架。需要在容器中运行客户端,容器提供了消息基础结构让提供者使用。下图演示了在使用消息提供者的情况下,异步消息如何在发送者和接收者之间交换。
使用消息提供者的客户端具有优点如下:
A、客户端能够扮演客户端或者服务角色
B、客户端能够切换消息传递给提供者
C、在客户端传递消息到最终接收者之前,它能够发送消息到一个或多个目的地。这些中间的消息接收者被称为actor,它们在消息的SOAPHeader对象中被指定。
D、客户端能够利用任何提供者支持的SOAP消息协议和影响消息类型与可靠性的‘服务质量’,以及消息传递服务的质量。
三、SOAP协议解析
1、SOAP 消息组成
所有的SOAP消息都使用XML编码,一条SOAP消息就是一个普通的XML文档,文档包括下列元素:
A、Envelope(信封)元素,必选,可把此XML文档标识为一条SOAP消息。
B、Header(报头)元素,可选,包含头部信息(包含了使消息在到达最终目的地之前,能够被路由到一个或多个中间节点的信息)。
C、Body(主体)元素,必选,包含所有的调用和响应信息。
D、Fault元素,位于Body内,可选,提供有关处理此消息所发生错误的信息。
E、Attachment(附件)元素,可选,可通过添加一个或多个附件扩展SOAP消息。
SOAP消息对象包括:
SOAP Envelope是代表消息的XML文件的根元素。它为消息如何处理、由谁处理定义了框架。XML内容从SOAP Envelope开始。
SOAP Header是添加特性到SOAP消息的基本机制。它可以容纳任意数目的扩展了基础协议的子元素。例如,header子元素可能会定义认证信息、事务信息、本地信息等等。处理消息的软件可以在没有事先约定的情况下,使用这个机制定义谁应该处理某个特性,以及该特性是强制的还是可选的。
SOAP Body是发给消息最终接收者的强制信息的容器。SOAP消息还可以容纳一个附件,它不一定非得是XML文件。
所有以上的元素均被声明于针对SOAP封装的默认命名空间中:
http://www.w3.org/2001/12/soap-envelope
以及针对 SOAP 编码和数据类型的默认命名空间:
http://www.w3.org/2001/12/soap-encoding
2、语法规则
A、SOAP消息必须用XML来编码
B、SOAP消息必须使用SOAP Envelope命名空间
C、SOAP消息必须使用SOAP Encoding命名空间
D、SOAP消息不能包含DTD引用
E、SOAP消息不能包含XML处理指令
3、SOAP基本消息结构
<?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:Fault>
...
...
</soap:Fault>
</soap:Body>
</soap:Envelope>
4、SOAP Envelope元素
SOAP Envelope 是SOAP消息结构的主要容器,也是SOAP消息的根元素,必须出现在每个SOAP消息中,用于把此XML文档标示为一条SOAP消息。
A、命名空间
在SOAP中,使用XML命名空间将SOAP标示符与应用程序特定的标示符区分开,将SOAP消息的元素的作用域限制在一个特定的领域。如果使用了不同的命名空间,应用程序会发生错误,并抛弃此消息。
SOAP消息必须拥有与命名空间相关联的一个Envelope元素,SOAP消息中所有元素必须由第一个命名空间进行限定。
SOAP 1.1规范如下:
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"
</soap-env:Envelope >
SOAP 1.2规范如下:
<soap:Envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
</soap:Envelope>
B、encodingStyle属性
SOAP的encodingStyle属性用于定义在文档中使用的数据类型,encodingStyle属性可出现在任何SOAP元素中,并会被应用到元素的内容及元素的所有子元素上。SOAP消息没有默认的编码方式。
语法
soap:encodingStyle="URI"
SOAP 1.1规范如下:
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"
Soap-env>
</ soap-env:Envelope >
SOAP 1.2规范如下:
<soap:Envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">
</soap:Envelope>
5、SOAP Header元素
SOAP Header元素应当作为SOAP Envelope的第一个直接子元素,必须使用有效的命名空间。Header还可以包含0个或多个可选的子元素,子元素称为Header项,所有的Header项都必须是完整修饰的,即必须由一个命名空间URI和局部名组成,不允许没有命名空间修饰的Header项存在。
Header元素用于与消息一起传输附加消息,如身份验证或事务信息。Header元素也可以包含某些属性。SOAP在默认的命名空间中定义了三个属性:actor,mustUnderstand以及encodingStyle。这些被定义在SOAP头部的属性可通知容器如何对SOAP消息进行处理。
<soap:Envelope
xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">
<soap:Header>
<m:Trans
xmlns:m="http://www.w3school.com.cn/transaction/"
soap:mustUnderstand="1">234</m:Trans>
</soap:Header>
...
</soap:Envelope>
例子包含了一个带有一个"Trans"元素的头部,值是234,此元素的"mustUnderstand"属性的值是"1"。
A、actor属性
通过沿着消息路径经过不同的端点,SOAP消息可从某个发送者传播到某个接收者。并非SOAP消息的所有部分均打算传送到SOAP 消息的最终端点,不过,另一个方面,也许打算传送给消息路径上的一个或多个端点。
SOAP的actor属性可被用于将Header元素寻址到一个特定的端点。
语法
soap:actor="URI"
<?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>
<m:Trans
xmlns:m="http://www.w3school.com.cn/transaction/"
soap:actor="http://www.w3school.com.cn/appml/">
234
</m:Trans>
</soap:Header>
...
...
</soap:Envelope>
B、mustUnderstand属性
SOAP的mustUnderstand属性可用于标识标题项对于要对其进行处理的接收者来说是强制的还是可选的。
假如向Header元素的某个子元素添加了"mustUnderstand="1",则它可指示处理此头部的接收者必须认可此元素。假如此接收者无法认可此元素,则在处理此头部时必须失效。
语法
soap:mustUnderstand="0|1"
<?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>
<m:Trans
xmlns:m="http://www.w3school.com.cn/transaction/"
soap:mustUnderstand="1">
234
</m:Trans>
</soap:Header>
...
...
</soap:Envelope>
C、encodingStyle属性
SOAP的encodingStyle属性与Envelope元素中相同。
6、SOAP Body元素
SOAP消息的Body块可以包含以下任何元素:
A、RPC方法及其参数
B、目标应用程序(消息接收者)专用数据
C、报告故障和状态消息的SOAP Fault
所有Body元素的直接子元素都称为Body项,Body项必须由命名空间修饰。
必需的SOAP Body元素可包含打算传送到消息最终端点的实际 SOAP消息。
SOAP Body元素的直接子元素可以是合格的命名空间。SOAP在默认的命名空间中("http://www.w3.org/2001/12/soap-envelope")定义了Body元素内部的一个元素。即SOAP的Fault元素,用于指示错误消息。
<?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:Body>
<m:GetPrice xmlns:m="http://www.w3school.com.cn/prices">
<m:Item>Apples</m:Item>
</m:GetPrice>
</soap:Body>
</soap:Envelope>
上面的例子请求苹果的价格。m:GetPrice和Item元素是应用程序专用的元素,并不是SOAP标准的一部分。而一个SOAP响应应该类似这样:
<?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:Body>
<m:GetPriceResponse xmlns:m="http://www.w3school.com.cn/prices">
<m:Price>1.90</m:Price>
</m:GetPriceResponse>
</soap:Body>
</soap:Envelope>
7、SOAP Fault元素
SOAP Fault元素用于在SOAP消息中传输错误及状态信息。如果SOAP消息需要包括SOAP Fault元素,SOAP Fault元素必须作为一个Body项出现,而且至多出现一次。SOAP Fault包括以下子元素:faultcode、faultstring、faultactor、detail。
子元素 | 描述 |
<faultcode> | 供识别故障的代码 |
<faultstring> | 可供人阅读的有关故障的说明 |
<faultactor> | 有关是谁引发故障的信息 |
<detail> | 存留涉及 Body 元素的应用程序专用错误信息 |
SOAP的Fault元素拥有下列子元素:
在下面定义的 faultcode 值必须用于描述错误时的 faultcode 元素中:
VersionMismatch | SOAP Envelope 元素的无效命名空间被发现 |
MustUnderstand | Header 元素的一个直接子元素(带有设置为 "1" 的 mustUnderstand 属性)无法被理解。 |
Client | 消息被不正确地构成,或包含了不正确的信息。 |
Server | 服务器有问题,因此无法处理进行下去。 |
8、SOAP附件
按照SOAP1.1规范的规定,SOAP消息可以包含XML格式的主SOAP信封,以及包含ASCII或二进制等任何数据格式的SOAP附件。如果SOAP消息包含附件,那么SOAP消息将是一个MIME编码的消息,包含SOAP内容和一个或多个其他类型的附件。因此SOAP消息实际上分为以下两种类型:
A、仅包含XML内容的消息
B、MIME编码的消息,包含初始的XML有效内容以及任何数量的附件。附件可以是任何其他类型的数据。
【MIME:Multi-purpose Internet Mail Extensions多用途Internet邮件扩展,是一组技术规范,其目的是使用不同字符集来传递文本,也可以在计算机之间传递各种各样的多媒体数据】
四、SOAP消息绑定
Web服务的有效负载通常包装在SOAP消息中,而SOAP消息结构由WSDL文档中的SOAP绑定定义确定。不同的调用方式和编码方式通过组合可以产生多种绑定样式,而每种样式的应用场景和对应的SOAP消息结构并不相同。如果没有正确的构造SOAP消息,则无法正确交换服务的有效负载。
SOAP Body提供了一种消息交换的机制,是SOAP消息的实际负载,可包含任意内容。SOAP消息体(SOAP Body)通过绑定服务调用方式(RPC或者Document)封装操作,绑定编码方式(Encoded或者Literal)序列化参数。SOAP消息的绑定样式由style、use和encodingStyle三个属性共同设置。style属性指定服务的调用方式,是采用RPC方式还是Document方式;use属性指定消息的编码方式,是采用Encoded方式还是采用Literal方式;而encodingStyle属性指定具体编码规则,例如可以指定SOAP编码规则、XML Schema编码规则等等,通常情况下都是采用XML Schema。
1、style属性
style属性描述了服务的调用方式,取值为”rpc”或者”document”,默认值为”document”。适用于SOAP Body元素的子元素(也可能是孙级元素)。此选项指定为WSDL文档中的soap:binding元素(通常情况下)或者soap:operation 的style属性。
A、RPC
RPC: 采用客户端/服务器方式(请求/响应),发送请求到服务器端,服务端执行方法后返回结果。优点是跨语言跨平台,缺点是编译时无法排错,只能在运行时检查。
SOAP消息本质上是一种从发送方到接收方的单向传输,但是SOAP经常组合到实现请求/响应机制中。要让RPC使用SOAP,必须遵循几条规则。首先,请求和响应消息必须被编码成结构类型。其次,对一个操作的每一个输入参数,都必须有一个同名元素(或输入结构的成员)作为参数。最后,对每一个输出参数,都必须有一个名称匹配的元素(或输出结构的成员)。
style=”rpc”指明遵从SOAP标准,在SOAP Body中封装RPC调用的请求和返回操作。对该类消息的约束是必须把操作的名称作为封装了对操作的请求和响应消息负载的根元素名称。对于SOAP请求消息,请求操作的名称是根据WSDL文档中的wsdl:operation元素命名,后者对应Web服务方法。对于SOAP响应消息,响应操作的名称是请求操作的名称后面追加"Response"构成。操作元素中的每个子元素表示一个参数,根据WSDL文档中的wsdl:part元素命名。RPC请求/响应消息的格式(省略了名称空间限定)如下:
RPC请求消息格式:
<SOAP信封>
<SOAP头部>
……
</SOAP头部>
<SOAP消息体>
<请求操作名称>
<参数名1>值</参数名1>
<参数名2>值</参数名2>
......
</请求操作名称>
</SOAP消息体>
</SOAP信封>
RPC响应消息格式:
<SOAP信封>
<SOAP头部>
……
</SOAP头部>
<SOAP消息体>
<响应操作名称>
<参数名1>值</参数名1>
<参数名2>值</参数名2>
......
</响应操作名称>
</SOAP消息体>
</SOAP信封>
B、Document
与RPC相比较Document方式在XML文件中不是做远程方法的映射,而是一份完整的自包含的业务文档。当服务器端收到这份文档后,先进行预处理(比如词汇的翻译和映射),然后再构造出返回消息。构造返回消息的过程中,往往不再是简简单单的一个方法调用,而是多个对象协同完成一个事务的处理,再将结果返回。
采用Document方式构造消息,SOAP Body元素中的内容完全由WSDL 中的 XML Schema定义,于是可以使用XML Schema验证SOAP Body元素中的内容。SOAP Body的子元素指定为消息部分在WSDL文档中定义的 wsdl:part 元素,该元素指向 XML Schema元素声明。通常wsdl:message不会包含多个wsdl:part 元素,所以SOAP Body 内容是真正的 XML 文档,尽管 WSDL 本身不禁止包含多个wsdl:part元素。Document请求/响应消息的格式(省略了名称空间限定)如下:
Document请求消息格式:
<SOAP信封>
<SOAP头部>
……
</SOAP头部>
<SOAP消息体>
<输入消息>
….
</输入消息>
….
</SOAP消息体>
</SOAP信封>
Document响应消息格式:
<SOAP信封>
<SOAP头部>
……
</SOAP头部>
<SOAP消息体>
<输出消息>
….
</输出消息>
….
</SOAP消息体>
</SOAP信封>
2、use属性
use属性描述了消息序列化的方式,取值为”encoded”或者”literal”,默认值是”literal”。 如果use=”encoded”,设置encodingStyle属性的值指定编码规则。如果use=”literal”,可以不用设置encodingStyle属性的值,通常情况都是使用默认的XML Schema。适用于出现在下一个级别的 Web 服务方法参数(或返回值)。此选项指定为WSDL文档中的 soap:body、soap:header、soap:fault和soap:headerfault元素的 use 属性。
A、Encoded
SOAP编码使用XML Schema的一个子集在XML文档与其所表示的数据之间进行绑定。SOAP 编码还使用href属性对文档中多次出现的元素的引用。SOAP编码和XML Schema编码完全不同的地方是数组。SOAP规范的5.4.2节指定了一种特别的机制来表示 XML 中的编程语言数组,它使用一种特殊的SOAP-ENC:Array类型。而XML Schema是通过设置element的minOccurs和maxOccurs属性值表示数组。例如使用这两种编码方式对整形数组的定义如下:
SOAP编码定义数组
<complexType name="ArrayOfInt">
<complexContent>
<restriction base="SOAP-ENC:Array">
<sequence>
<element name=”item” type=”xsd:int” minOccurs=”0” maxOccurs=”unbounded”/>
</sequence>
<attribute ref=" SOAP-ENC:arrayType" wsdl:arrayType="xsd:ArrayOfInt[]"/>
</restriction>
</complexContent>
</complexType>
XML Schema定义数组
<complexType name="ArrayOfInt">
<sequence>
<element name=”item” type=”xsd:int” minOccurs=”0” maxOccurs=”unbounded”/>
</sequence>
</complexContent>
</complexType>
B、Literal
与Encoded相比,Literal采用XML Schema编码,而不是SOAP编码;Literal编码不需要数据类型属性。数据根据 WSDL 文档中指定的 XML Schema定义或导入 WSDL 文档的 XML Schema定义逐字进行格式设置。使用Literal方式编码add方法的消息格式如下:
<op:add xmlns:op=”http://act.buaa.edu.cn/add”>
<a>
<item>45</item>
<item>36</item>
</a>
<b>
<item>235</item>
<item>67</item>
</b>
</op:bdd>
3、encodingStyle属性
WSDL规范中的encodingStyle属性和SOAP规范中的encodingStyle 属性定义是一样的。encodingStyle属性的值是一个URIs列表,由单个空格隔开,每个URI代表着一种消息的编码规则,它们按照编码的限制从强到弱排序。encodingStyle属性是用来指定SOAP消息的编码规则,也就是序列化的格式和类型系统。如果use=”encoded”,encodingStyle属性的值为”http://schemas.xmlsoap.org/soap/encoding/”,则表示使用SOAP规范的第5节的编码,这一节定义了将编程语言的类型映射到 XML 的基本机制。如果use=”literal”则使用外部提供的类型系统,也可以通过encodingStyle属性来指定Schema,但是通常情况下使用XML Schema来编码SOAP消息。
这其中的缘故是因为SOAP规范是在采用XML Schema规范之前编写的。因此,原始的SOAP规范必须提供一种方法来指明编码类型信息。然而,自从采用XML Schema之后,大多数语言使用自己的从XML Schema到编程语言类型之间的映射(或序列化规则),这使得SOAP编码变得过时。因此,推荐不要采用SOAP编码,而是采用使用Literal编码,在Literal映射中由 XML Schema 文档(通常是WSDL文档的形式)从外部指定映射。
五、消息构造
style和use属性都有两个值,通过它们之间的不同组合,就可以产生四种绑定样式,分别是rpc-literal、rpc-encoded、document-literal和document-encoded。为了使Document绑定样式能够支持RPC绑定样式的调用,增加了一种包装(Wrapped)样式,它只不过是对Document样式的使用进行了约束。这样就又增加了两种绑定样式, document-literal-wrapped和document-encoded-wrapped,一共合起来就有6种绑定样式。下面通过一个加法服务为例来说明在不同绑定样式下的SOAP消息构造,服务的定义如下:
从服务的定义可以看出,输入操作的操作名为add,有a和b两个整数类型的输入参数。根据WSDL规范返回操作的操作名应该为addResponse,有一个整数类型的输出参数,假设命名为return。
操作的序列化是需要名称空间限定的。如果是RPC调用方式,则名称空间是soap :body的namespace属性值;如果是Document调用方式,名称空间则是输入消息引用的Schema的targetNamespace属性值。参数的序列化是否需要名称空间限定取决于Schema定义时elementFormDefault属性的值。如果值为”qualified”则表示需要限定,名称空间为Schema的targetNamespace属性值;如果值为”unqualified”表示不用限定,默认值为”unqualified”。操作序列化后的元素作为SOAP消息体的子元素,而每个参数序列化后的元素都是作为操作元素的子元素,排列的顺序和操作的参数定义顺序一样。
假定以下所有SOAP消息都是在如下条件下构造:
A、如果use= ”encoded”则encodingStyle的值为”http://schemas.xmlsoap.org/soap/encoding/”,如果use= ”literal”则使用” http://www.w3.org/2001/XMLSchema”编码。
B、如果style=”rpc”则wsdl:part 部分的类型引用使用type属性,如果style=”document”则使用element属性。
1、rpc-literal绑定样式
WSDL描述:
<wsdl:definitions xmlns:wsdl= " http://schemas.xmlsoap.org/wsdl/"
xmlns:ns="http://act.buaa.edu.cn/add" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://act.buaa.edu.cn/add">
<wsdl:message name=”addRequest”>
<wsdl:part name=”a” type=”xsd:int”/>
<wsdl:part name=”b” type=”xsd:int ”/>
</wsdl:message>
<wsdl:message name=”addReponse”>
<wsdl:part name=”return” type=”xsd:int”/>
</wsdl:message>
<!--Just assume it's rpc-literal. -->
……
SOAP请求消息:
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<op: add xmlns:op=“http://act.buaa.edu.cn/add”>
<a>12</a>
<b>45</b>
</op: add >
</env:Body>
</env:Envelope>
优点:WSDL 描述和SOAP消息基本达到了尽可能地简单易懂的要求;服务的操作名出现在SOAP消息的Body中,这样接收者就可以很轻松地把消息发送到方法的实现;没有类型编码,提高了吞吐量,减少了处理的性能开销。
缺点:在RPC模型中XML仅仅被用于描述方法的信息,不能充分利用XML的功能去描述和验证一份业务文档 ;不能使用Schema简单地检验此消息的有效性,因为只有参数a和b是Schema 中定义的内容,其余的 env:body 内容(例如add元素)都来自 WSDL 定义;无法直接从消息中获得参数的类型信息;RPC样式对请求/响应消息的模式捆绑,使得服务与客户端之间耦合性增加,一旦方法发生变化,客户端就需要做相应的改动;相对异步调用方式而言,RPC样式下服务调用通常是同步的。
2、rpc-encoded 绑定样式
SOAP请求消息:
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi=” http://www.w3.org/2001/XMLSchema-instance” >
<env:Body>
<op: add xmlns:op=“http://act.buaa.edu.cn/add”>
< a xsi:type=”xsd:int”>12</a>
<b xsi:type=”xsd:int”>45</b>
</op: add >
</env:Body>
</env:Envelope>
与rpc-literal绑定样式唯一的区别就是请求消息的参数部分编码方式不一样。消息中的每个参数都有类型编码(比如 <a xsi:type=”int”>12</a>),直接从消息就可以知道参数的数据类型。但是这些类型信息降低了吞吐量和消息处理的性能。尤其是在大数据的情况下,性能开销明显。
rpc-encoded绑定样式主要在重载、数据图形和多态的情况下使用。WSDL 允许重载的操作,但是当参数个数相同的情况下,因为Literal编码方式没有类型信息,无法定位方法,所以rpc-literal就不支持这样的重载。数据图形的标准方式是使用href 标记,它是 rpc-encoded的样式。多态所生成的 SOAP 消息必须包含类型编码信息,这样接收终端才能知道它所接收的是父类的哪一个扩展。
3、document /literal绑定样式
WSDL 描述:
<wsdl:definitions xmlns:wsdl= " http://schemas.xmlsoap.org/wsdl/"
xmlns:ns="http://act.buaa.edu.cn/add" targetNamespace=" http://act.buaa.edu.cn/add ">
<wsdl :types>
<xs:schema elementFormDefault="qualified" targetNamespace="http://act.buaa.edu.cn/add" xmlns:xs="http://www.w3.org/2001/XMLSchema" >
<xs:element name="a" type="xs:int " />
<xs:element name="b" type="xs:int " />
<xs:element name="return" type="xs:int " />
</xs :schema>
</wsdl :types>
<wsdl:message name=”addRequest”>
<wsdl:part name=”parameter1” element=”ns:a”/>
<wsdl:part name=”parameter2” element=”ns:b”/>
</wsdl:message>
<wsdl:message name=”addReponse”>
<wsdl:part name=”parameter” element=”ns:return”/>
</wsdl:message>
<!--Just assume it's document-literal. -->
……
SOAP 请求消息:
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:op=“http://act.buaa.edu.cn/add”>
<env:Body>
<op:a>12</op:a>
<op:b>45</op:b>
</env:Body>
</env:Envelope>
请求消息的参数元素添加了名称空间限定,这是因为输入消息在WSDL文档的Schema中定义,而且Schema的elementFormDefault=”qualified”。也就是说参数元素必须使用Schema的名称空间"http://act.buaa.edu.cn/add"进行限定。
优点:没有操作和类型编码信息,减少了消息的数据量,提高了消息处理性能;env:Body中每项内容都定义在Schema中,可以用任何XML检验器检验此消息的有效性。
缺点:WSDL文档变得比较复杂,这不过是一个非常小的缺点,因为 WSDL 并没有打算由人来读取;SOAP消息中没有提供服务操作的名称,一些特定的程序代码在分发消息时可能会变得复杂,并且有时变得不可能。如果使用HTTP作为底层传输协议,可以使用SOAPAction属性绑定操作的名称来解决消息分发的问题。虽然大多数情况下都是使用HTTP协议来传输SOAP消息,但是这种方法绑定了底层传输协议,限制了其他传输协议的使用。
4、document /encoded绑定样式
SOAP 请求消息:
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi=” http://www.w3.org/2001/XMLSchema-instance”
xmlns:op=“http://act.buaa.edu.cn/add”>
<env:Body>
<op:a xsi:type=”xsd:int”>12</op:a>
<op:b xsi:type=”xsd:int”>45</op:b>
</env:Body>
</env:Envelope>
与document-literal绑定样式唯一的区别就是请求消息的参数部分编码方式不一样。引入了类型编码,降低了吞吐量和消息处理的性能。这种绑定样式不被大多数Web服务实现平台支持。
5、document-literal-wrapped绑定样式
WSDL描述:
<wsdl:definitions xmlns:wsdl= " http://schemas.xmlsoap.org/wsdl/"
xmlns:ns="http://act.buaa.edu.cn/add" targetNamespace=" http://act.buaa.edu.cn/add ">
<wsdl :types>
<xs:schema elementFormDefault="qualified" targetNamespace="http://act.buaa.edu.cn/xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema" >
<xs:element name="add">
<xs:complexType>
<xs:sequence>
<xs:element name="a" type="xs:int " />
<xs:element name="b" type="xs:int " />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="addResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="return" type="xs:int " />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs :schema>
</wsdl :types>
<wsdl:message name=”addRequest”>
<wsdl:part name=”parameter” element=”ns:add”/>
</wsdl:message>
<wsdl:message name=”addReponse”>
<wsdl:part name=”parameter” element=”ns:addResponse”/>
</wsdl:message>
<!--Just assume it's document-literal-wrapped. -->
……
SOAP请求消息:
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<op: add xmlns:op=“http://act.buaa.edu.cn/add”>
<op:a>12</op:a>
<op:b>45</op:b>
</op: add >
</env:Body>
</env:Envelope>
此时SOAP Body中第一个元素的名称并不是操作的名称,而是Schema中的元素的名称。Schema中的元素的名称可以与操作名相同,也可以不同。如果取相同则是一种将操作名放入SOAP消息的巧妙方式。
虽然此SOAP 消息看起来与 rpc-literal的 SOAP 消息是完全一样的(如果不考虑名称空间限定),但是这两种消息之间存在着微妙的区别。在 rpc-literal的 SOAP 消息中,<env:Body>下的<op:add>根元素是操作的名称。在document-literal-wrapped的SOAP 消息中,<op:add>元素是单个输入消息的组成部分引用的元素的名称。
document-literal-wrapped样式规定Document绑定操作的输入消息和输出消息都只有一个wsdl:part部分;该部分使用element属性引用一个元素;该元素是复杂类型并且没有属性;该元素的名称和操作的名称必须一样。
优点:包装行为吸取了RPC样式的一个重要优点,即RPC样式中SOAP消息体可以直接通过与之关联的服务操作名称来命名,同时又摒弃了RPC样式的不足之处;可以利用WSDL文档类型部分的Schema文档直接来验证SOAP消息体;只要在Schema中定义了明确的数据结构,如何构建SOAP消息体具有很大的灵活性;由于业务数据是自包含的,显然文档模型更利于采用异步处理。
缺点:WSDL 较为复杂, 但是这仍然是一个非常小的缺点。使得服务调用的复杂度有所增加,尤其是在API级别。针对程序开发人员来说,基于包装的document绑定样式的服务编写客户端代码也许就变成了一项极具挑战性的工作。在SOAP消息体的XML包装元素中必须拥有一个服务操作的名称,因此包装版本不支持重载的服务操作。实际上,针对一个既定的元素名称也只能够有一个服务操作。
6、document-encoded-wrapped绑定样式
SOAP请求报文
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi=” http://www.w3.org/2001/XMLSchema-instance” >
<env:Body>
<op: add xmlns:op=“http://act.buaa.edu.cn/add”>
<op:a xsi:type=”xsd:int”>12</op:a>
<op:b xsi:type=”xsd:int”>45</op:b>
</op: add >
</env:Body>
</env:Envelope>
与document -literal-wrapped绑定样式唯一的区别就是请求消息的参数部分编码方式不一样。引入了类型编码,降低了吞吐量和消息处理的性能。
六、SOAP 附件
实际应用中需要和各种附件(如图像、图画、xml 文档等)一起发送SOAP消息。附件数据通常是特殊的二进制格式。 带附件的SOAP规范详细说明了使用“MIME multipart/related” 媒体类型和URI模式引用MIME部件。并不是所有的Web服务工具箱都提供SOAP附件支持。Microsoft提出了另一个基于DIME的附件解决方案
1、SOAP消息包
带附件的SOAP消息包如下:
SOAP消息包包含XML格式的主SOAP消息以及SOAP信封中未定义但与消息有关的任意数据格式(例如 gif、jpg 和 xml 等)的其它实体。
SOAP消息包是用MIME的Multipart/related 媒体类型构建的,每个部件都嵌入MIME边界(在Context-Type报头中定义)。每个 MIME部件都有报头信息比如Context-Type 它指定嵌入这MIME部件的数据的类型)、Content Transfer-encoding(指定用于这个MIME部件的编码)、 Content-ID或Content-Location(作为从MIME包的任何地方引用这些内容的标识符)。MIME消息的根部件包含SOAP信封,Content-Type被设置为text/xml。
2、SOAP对附件的引用
SOAP报头和SOAP消息的主体都可以引用消息包中的其它实体。根据 SOAP 1.1 编码规则,SOAP的“href”属性可用于引用任何资源。
任何资源都可以使用Content-ID或者Content-Location引用。如果使用Content-ID作为标识符,那么模式属性(例如href)必须使用URL模式CID。如果使用Content-Location作为标识符,那么必须根据 带附件的 SOAP规范中指定的规则进行解析,其中解析是基于下列要素之一:
绝对URI引用 ― 绝对URI是SOAP信封中的Content-Location和“href”属性中指定的。
相对URI引用― MIME消息主报头的Content-Location中指定了一个基础(base)URI,对所有的相对URL都是使用这个基础URL进行引用。
不带基础URI的相对URI ― 主报头的Content-Location中没指定基础URI,但使用消息的基础URL,并且对所有的相对URL都是使用这个基础URL进行引用。
通常由SOAP处理器决定是否需要解析URI。而且,消息包中可能有在SOAP信封中没有URI引用的附件。
3、HTTP绑定
如果HTTP绑定被SOAP用来发送附件,就需要修改HTTP报头使其包含来自SOAP MIME包报头的Content-Type信息。HTTP报头中不包含来自MIME包报头的其它报头信息。Apache忽略了报头信息比如Content-Transfer-Encoding和MIME Version 。所有的MIME部件都包含SOAP信封部件和其它附件,构成HTTP主体。另一方面,对于 SMTP绑定来说,所有的多部件MIME报头都会被作为SMTP报头的一部分存储。这样,SOAP处理器和应用程序就应该知道这些报头与不同种类的 SOAP 绑定的不兼容性。
4、WSDL支持
WSDL支持描述带附件的Web服务。
<binding name="DocManagementService_Binding" ... />
<operation name="SubmitArrayOfDocuments">
<soap:operation soapAction="http://www.DocManagementService.com/SubmitArrayOfData"/>
<input>
<mime:multipartRelated>
<mime:part>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:DocManagement-service"
parts="SubmitArrayOfDocuments_inType1 SubmitArrayOfDocuments_inType2"
use="encoded"/>
</mime:part>
<mime:part>
<mime:content part="SubmitArrayOfDocuments_inType3" type="*/*"/>
</mime:part>
</mime:multipartRelated>
</input>
<output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:DocManagement-service"
use="encoded"/>
</output>
</operation>
</binding>
binding元素的input和output元素被扩展,并且包含了带有不同MIME部件的MIME:multipart-related,其中一个MIME部件用于 SOAP主体,其它的用于附件。每个带附件的MIME部件也都可以指定其内容类型。
七、SOAP协议实例
在下面的例子中,一个GetStockPrice 请求被发送到了服务器。此请求有一个StockName参数,而在响应中则会返回一个Price参数。此功能的命名空间被定义在此地址中: "http://www.example.org/stock"
SOAP 请求:
POST /InStock HTTP/1.1
Host: www.example.org
Content-Type: application/soap+xml; charset=utf-8
Content-Length: nnn
<?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:Body xmlns:m="http://www.example.org/stock">
<m:GetStockPrice>
<m:StockName>IBM</m:StockName>
</m:GetStockPrice>
</soap:Body>
</soap:Envelope>
SOAP 响应:
HTTP/1.1 200 OK
Content-Type: application/soap+xml; charset=utf-8
Content-Length: nnn
<?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:Body xmlns:m="http://www.example.org/stock">
<m:GetStockPriceResponse>
<m:Price>34.5</m:Price>
</m:GetStockPriceResponse>
</soap:Body>
</soap:Envelope>
SOAP 1.1协议:
请求的协议体:
POST /weather HTTP/1.1
Accept: text/xml, multipart/related
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://weather.itheima.com/WeatherInterface/getWeatherByCityNameRequest"
User-Agent: JAX-WS RI 2.2.4-b01
Host: 127.0.0.1:54321
Connection: keep-alive
Content-Length: 235
<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:getWeatherByCityName xmlns:ns2="http://weather.itheima.com">
<cityName>北京</cityName>
</ns2:getWeatherByCityName>
</S:Body>
</S:Envelope>
响应的协议体:
HTTP/1.1 200 OK
Transfer-encoding: chunked
Content-type: text/xml; charset=utf-8
Date: Fri, 09 Oct 2015 07:23:29 GMT
<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:getWeatherByCityNameResponse xmlns:ns2="http://weather.itheima.com">
<WeatherInfo>今天风很大</WeatherInfo>
</ns2:getWeatherByCityNameResponse>
</S:Body>
</S:Envelope>
SOAP 1.2协议:
请求的协议体:
POST /weather HTTP/1.1
Accept: application/soap+xml, multipart/related
Content-Type: application/soap+xml; charset=utf-8;
action="http://weather.itheima.com/WeatherInterface/getWeatherByCityNameRequest"
User-Agent: JAX-WS RI 2.2.4-b01
Host: 127.0.0.1:54321
Connection: keep-alive
Content-Length: 233
<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://www.w3.org/2003/05/soap-envelope">
<S:Body>
<ns2:getWeatherByCityName xmlns:ns2="http://weather.itheima.com">
<cityName>北京</cityName>
</ns2:getWeatherByCityName>
</S:Body>
</S:Envelope>
响应的协议体:
HTTP/1.1 200 OK
Transfer-encoding: chunked
Content-type: application/soap+xml; charset=utf-8
Date: Fri, 09 Oct 2015 07:54:53 GMT
<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope xmlns:S="http://www.w3.org/2003/05/soap-envelope">
<S:Body>
<ns2:getWeatherByCityNameResponse xmlns:ns2="http://weather.itheima.com">
<WeatherInfo>今天风很大</WeatherInfo>
</ns2:getWeatherByCityNameResponse>
</S:Body>
</S:Envelope>