2.1 服务
2.1.1 SOAP服务
Web服务及其协议栈是对SOA理念最初的一种尝试。它将服务实现为一种可以自包含、自描述及模块化的Web组件,并通过Web进行发布、查找和调用。WSDL、SOAP和UDDI是对Web服务来说最重要的三个协议。WSDL定义了如何描述一个Web服务;SOAP定义了怎样调用并触发一个Web服务;UDDI则定义了如何发布、管理及查找Web服务的描述信息。为了和后面介绍的REST服务区分,本书将使用SOAP的Web服务统称为SOAP服务。
W3C(万维网联盟)对SOAP服务的定义如下。
定义2.1 SOAP服务(SOAP service):一种支持机器间通过网络交互的软件系统。它的接口描述是机器可处理的(通常采用WSDL语言)。服务之间的交互采用SOAP消息,并通过Web相关标准及基于HTTP协议的XML消息交换方式实现交互(Haas et al. 2004)。
作为一个新兴的分布式计算平台,SOAP服务除了达成互操作方面的承诺,在可靠通信、安全、事务、管理、编程模型和协作协议等方面都有相应的规范和协议。
2.1.1.1 SOAP
SOAP(simple object access protocol)是“简单对象访问协议”的简称,它的初始目的是提供一种基于XML文本的通信协议,以实现DCOM和CORBA之间的互操作。但是,随着Web服务理念和技术的逐渐成熟,SOAP规范的重心很快从对象转移到通用的XML消息处理框架上。这种重心的变化给SOAP这一缩写词的定义带来了一点小问题。SOAP 1.2工作组沿用了SOAP这个名称,但决定不再使用该词的起始全称以免误导开发人员,因此在最新的SOAP 1.2规范中,其正式的定义并不提及“object”一词。
SOAP是一个轻型的分布式计算协议,它允许在一个分布式的环境中交换信息。SOAP没有与硬件平台、操作系统、编程语言或网络硬件平台捆绑。与其他分布式计算系统不同的是,SOAP建立在开放式标准的顶部,如HTTP和XML。SOAP用基于文本的XML协议与分布式系统通信,而非用其他分布式计算协议(如CORBA、RMI和DCOM)使用的二进制格式。这使得SOAP具有跨硬件平台、操作系统、编程语言和网络硬件平台的高度互操作性。SOAP可以在HTTP上传输,HTTP允许它利用已有的基础设施投资,如Web服务器、代理服务器和防火墙。SOAP也可以用其他协议(如SMTP和JMS)进行传输。
SOAP规范的主要目标是简单性和可扩展性。
(1)简单性:SOAP规范定义的消息结构非常简单,除了这个基本消息结构,SOAP没有定义额外的表述结构标准,没有定义自己的编码格式,也没有定义自己的传输协议;而且,SOAP还避免了许多与组件模型有关的复杂功能,如分布式垃圾收集、对象引用、对象激活及消息批处理等。
(2)可扩展性:SOAP使用XML来封装远程调用和交换的数据,SOAP非常具有弹性,因为它可以使用XML来封装所有的数据,并且扩充它的功能和意义,同时能够通过标准的XML分析技术来进行消息解析。
2.1.1.2 SOAP消息处理模型
最简单的SOAP消息处理模型如图2-1所示:XML格式的SOAP消息通过某种网络通信协议(如HTTP、SMTP等)在SOAP消息发送方和接收方之间传送。
图2-1 最简单的SOAP消息处理模型
高级SOAP消息处理模型如图2-2所示:SOAP消息从起始发送方发出后,经过多个中间节点到达最终接收方。
图2-2 高级SOAP消息处理模型
SOAP消息中间节点(简称SOAP节点)位于起始发送方和最终接收方之间,截获SOAP消息并进行相应的处理。大体来说,SOAP消息由消息头和消息体构成,中间节点只能对SOAP消息头进行处理和修改,而无权处理和修改SOAP消息体,这点在SOAP1.2规范中有明确定义。
在处理消息时,SOAP节点将会承担一个或多个角色,SOAP节点的角色决定节点如何处理SOAP消息头。当SOAP节点接收到一条消息时,它首先必须确定其自身的角色,其次看自己是否必须处理该消息(由消息头中的mustUnderstand属性决定)。
2.1.1.3 SOAP消息结构
SOAP消息结构的基本模型如图2-3所示:整个SOAP消息包含在一个信封中,信封内有一个SOAP消息头和一个SOAP消息体,其中SOAP消息头是可选的,消息头和消息体可以包含多个条目,SOAP消息体可以包含SOAP故障。
图2-3 SOAP消息结构基本模型
上述SOAP消息的概念模型如图2-4所示。
对SOAP消息概念的解释如下。
(1)信封(Envelope):信封是SOAP消息的根元素,包含一个可选的SOAP消息头元素和一个必需的SOAP消息体元素。
(2)消息头(Header):消息头是可选元素,其中可以包含多个任意格式的项,比如描述安全性、事务处理、会话状态信息的项。
图2-4 SOAP消息概念模型
(3)消息体(Body):消息体是必需元素,代表实际的消息负载,可以包含多个任意格式的项,这些项可以采用两种XML结构风格,即文档风格或RPC风格,本节后续会详细讨论这两种风格。
(4)故障(Fault):故障是可选元素,用于携带出错信息。故障只能作为消息体元素的直接子元素,并且一个消息体元素只能包含一个故障元素。
下面详细介绍SOAP消息的各组成部分。
1.SOAP信封
SOAP信封是SOAP消息的根元素,SOAP信封包含两个子元素:一个是可选的消息头,另一个是必需的消息体。在SOAP信封元素中,可以通过XML命名空间定义SOAP消息的版本信息,也可以通过XML命名空间定义编码风格。
1)soapEnv:SOAP消息版本
soapEnv命名空间标明了SOAP消息的版本,其中:
SOAP1.1命名空间的URI是:http://schemas.xmlsoap.org/soap/envelope/;
SOAP1.2命名空间的URI是:http://www.w3.org/2003/05/soap-envelope/。
按照SOAP规范,一个SOAP信封必须与上述命名空间中的一个相关联,不遵守上述命名空间声明的SOAP消息会被视为一个版本错误。
2)encodingStyle:编码风格
SOAP消息通常需要定义的另一个命名空间是SOAP消息的编码风格encodingStyle(关于编码风格详见2.1.1.4节)。
SOAP1.1编码风格的URI是:http://schemas.xmlsoap.org/soap/encoding/;
SOAP1.2编码风格的URI是:http://www.w3.org/2003/05/soap-encoding/。
2.SOAP消息头
SOAP消息头是可选的。如果SOAP信封中包含消息头元素,那么它必须作为根元素信封的第一个子元素出现。SOAP消息头也是可扩展的,用户可以自行添加一些用于描述安全性和事务处理的数据,Web服务规范WS-Security、WS-Transaction等都是SOAP消息头的扩展规范。
SOAP消息头主要有如下几个属性。
1)role
在SOAP规范中,一条SOAP消息在到达最终目的地之前,可能会通过多个SOAP节点。每个SOAP节点都能接收和处理SOAP消息并将它转发到下一个SOAP节点。通过设定role属性,可以指明由哪个SOAP节点来处理SOAP消息头。如果选择默认的role属性项,则表明SOAP消息头将被定位至最终的SOAP节点。
2)mustUnderstand
该属性指明接收消息的SOAP节点是否必须理解和处理SOAP消息头。如果mustUnderstand=“false”,表明接收消息的SOAP节点可以忽略SOAP消息头;如果mustUnderstand=“true”,表明接收消息的SOAP节点必须理解并处理SOAP消息头,否则必须返回一个SOAP故障。
3)relay
当relay属性值为“true”时,未被处理的SOAP消息头必须被继续发送。
3.SOAP消息体
SOAP消息体包含的是SOAP消息的实际负载,因此在SOAP消息中是必需元素。SOAP消息体由SOAP消息的最终接收方接收并处理。
SOAP消息体中的项可以包含一个可选的encodingStyle属性,代表该项的编码风格,如果消息体中的某项为自己定义了编码风格属性,那么该属性将替代(override)在信封中定义的编码风格。
SOAP消息体可以包含任意内容,但SOAP规范定义了两种消息风格供发送者和接收者使用。这两种消息风格分别被称为RPC风格(RPC style)和文档风格(document style)。SOAP消息体可以包含可选的SOAP故障。
RPC风格的消息遵从SOAP标准,封装的是RPC调用的请求和返回消息,对该类消息的主要约束是必须把操作名称作为封装了对操作的调用请求和返回消息负载的根元素名称,如图2-5和代码2-1(RPC风格的调用请求消息)、代码2-2(RPC风格的返回消息)所示。一般RPC风格的消息可以由SOAP工具自动产生。而由文档风格的消息封装的XML文档可以是消息发送方和消息接收方约定的任意格式。
图2-5 RPC风格的调用请求消息和返回消息
4.SOAP故障
SOAP故障用于携带出错信息,SOAP故障只能作为SOAP消息体的直接子元素出现,且至多出现一次。
SOAP故障包含如下几个子元素。
(1)Code(SOAP 1.1规范中为faultCode;SOAP 1.2规范中为Code):Code元素在故障元素中是必需的。Code元素中包含一个必需的Value子元素和一个可选的Subcode子元素。Value元素的可能取值为故障代码;每个Subcode元素是迭代的,它由一个必需的Value子元素和一个可选的Subcode子元素组成。表2-1列出了SOAP 1.2规范中的故障代码及其含义。
表2-1 SOAP 1.2规范中的故障代码及其含义
(2)Reason(SOAP1.1规范中为faultString;SOAP1.2规范中为Reason):Reason元素在故障元素中是必需的,该元素不是为处理程序设定的,而是为故障代码提供可以读懂的故障解释;Reason元素可以存在多个Text子元素。
(3)Node:Node元素在故障元素中也是可选的,它指明了在消息传递路径中,哪个SOAP节点引发了故障。
(4)Role:Role元素在故障元素中是可选的,它指明了引发故障的SOAP节点的角色。
(5)Detail:Detail元素在故障元素中是可选的。在SOAP1.1规范中,如果SOAP消息体元素的内容无法被处理,那么就需要用到Detail元素,也就是说,Detail元素提供了与消息体元素相关的应用程序的故障信息;SOAP 1.2规范则不再区分是消息头故障还是消息体故障。
2.1.1.4 SOAP消息编码
SOAP信封及SOAP消息体中的元素都可以设定encodingStyle属性,该属性指明了SOAP消息的编码风格,在信封中设定的编码风格作用于整个SOAP消息体,而消息体包含的元素自己设定的编码风格仅作用于自身的范围。现在,SOAP规范并没有限定encodingStyle属性的取值,但常见的是SOAP编码风格和字面(literal)编码风格。所谓字面编码就是没有编码风格(不设定encodingStyle属性),SOAP消息的格式完全由交互双方自行约定。而SOAP编码风格的消息必须遵从SOAP编码风格。
SOAP 1.1规范的第5部分定义了SOAP编码风格。到SOAP 1.2规范时,SOAP编码风格不再是SOAP规范的一部分,而是单独成为一个可选规范。SOAP 1.2编码风格的URI为http://www.w3.org/2003/05/soap-encoding,如果encodingStyle属性被赋予该URI,则表明使用的是SOAP编码风格。
SOAP编码的原理:在SOAP模式的约束下,应用级对象(如Java对象、C#对象)基于SOAP数据模型规则被转换为SOAP编码的XML消息负载。在发送者和接收者不另行约定消息内容模式的情形下,它们可以使用SOAP编码来实现从应用级对象到XML消息文档的自动处理,在转换过程中,程序设计语言的类型被转换为XML Schema。
下面简单介绍一下SOAP编码是如何把程序设计语言的类型转换为XML Schema的。程序设计语言的类型一般可以分为简单类型和复合类型,简单类型是无法包含其他元素的类型。
1.简单类型的编码
简单类型都可以采用叶节点元素来表示,如代码2-3所示。
程序设计语言中的枚举类型直接对应XML Schema中的枚举类型。代码2-4给出的是Month枚举类型。
2.复合类型的编码
SOAP编码提供了对结构(Structs)和数组(Arrays)两种复合类型的编码。结构允许把不同类型的值混合在一起,每个值用唯一的存储器存储和接收;数组由多个值组成,这些值用一个顺序位置号存储和检索。
1)结构
在结构类型中,允许把不同类型的值混合在一起,但同一结构中的不同组成元素的名称必须是唯一的。SOAP编码把结构映射为XML Schema复合类型,如代码2-5所示。
2)数组
数组采用顺序位置来标识多个值。数组被定义为“soapEnv:Array”类型或从“soapEnv:Array”衍生的类型。数组表示元素值,对元素名没有特别的约束。数组中包含的元素可以是简单类型或复合类型,数组成员值也可以是数组。数组可以单引用或多引用。代码2-6给出的是一个整型数组的片段示例。
代码2-6中数组元素的类型也可以直接通过“soapEnv:arrayType”属性来指明,如代码2-7所示。
2.1.1.5 SOAP和网络传输协议的绑定
SOAP消息可以和各种网络传输协议绑定,如SOAP 1.0规范最初规定的HTTP协议,以及新SOAP规范和主流的SOAP供应商推出的SMTP、FTP、POP3、BEEP、JMS、MSMQ等协议。
由于当前几乎所有的操作系统都支持HTTP,因此虽然HTTP绑定是可选的,几乎所有SOAP实现方案都支持HTTP绑定,SOAP规范中也只对SOAP的HTTP绑定进行了详细说明。
本节介绍如何实现SOAP的HTTP绑定,SOAP会遵守HTTP请求/响应消息交换模式,在HTTP请求中提供SOAP请求参数,并且在HTTP响应中包含SOAP响应参数。
1.SOAP请求
根据SOAP Web方法(SOAP Web method)[1]的定义,在与HTTP等网络传输协议进行绑定实现SOAP消息传输时,需要指出所使用的SOAP Web方法,如GET、POST等。GET方法通常被用来取得Web上的信息,POST方法则被用来将信息从客户端传送给服务器,然后利用POST方法所传送的信息就会被服务器上的应用程序使用。利用GET方法只能传送特定类型的信息,而利用POST方法则可以传送各种类型的数据。
代码2-8是用HTTP POST方法表示的SOAP请求。
在这个代码中,第一行包含了三个不同的部分:请求的方法(POST)、请求的URI(/OrderStatus),以及使用协议的版本(HTTP/1.1)。
第二行是请求消息被传送的目标服务器的地址。
第三行包含了媒体类型和字符编码类型,text/xml表示负载是纯文字形式的XML。当HTTP信息中包含SOAP数据主体时,HTTP应用程序必须遵循RFC 2376的内容型别text/xml。负载指的是被传送的数据。SOAP要求必须使用text/xml作为媒体类型;SOAP使用utf-8作为字符编码类型。
第四行则指定负载的大小,以位为单位。
第五行的SOAPAction在SOAP 1.2规范中是可选的,用来指定SOAP HTTP要求的目标。如果SOAP消息的接收节点希望在处理SOAP信封元素之前就能了解SOAP消息的一些确切信息,则可以通过HTTP绑定中的SOAPAction来实现。
2.SOAP响应
代码2-9是代码2-8中请求消息的HTTP响应。
代码2-9的第一行包含了状态码(200)及与状态码相关联的信息(OK)。SOAP HTTP完全遵循HTTP状态码的语法,主要包括如下几种状态码。
(1)成功状态码。2XX HTTP成功状态码用来说明SOAP请求消息已经被接收或被成功处理。其中,200 OK表示请求消息被成功接收,并返回一个响应消息,该响应消息不是一个错误,而是一个正常的SOAP响应;202 Accepted表示请求消息被成功接收和处理,但不会返回响应消息。
(2)错误状态码。通常情况下,HTTP使用4XX表示SOAP消息传送中在客户端出现的错误,使用5XX表示在服务器端出现的错误。其中,400 Bad Request表示HTTP请求或SOAP消息中的XML文档格式错误;405 Method Not Allowed表示服务提供者不支持请求消息中选用的Web方法;415 Unsupported Media Type表示服务提供者不支持HTTP POST消息中Content-Type选用的值。500 Internal Server Error表示服务器内部错误,或者SOAP响应消息包含了SOAP故障元素。
(3)3XX Redirection。它表示请求的资源被迁移了,需要使用传回的相关联的Location头字段的URI值重试请求。