SOAP技术与B2B应用集成(2)
SOAP的消息结构与数据的组织方法
本文最初由 IBM developerWorks 中国网站发表,其网址是
http://www.ibm.com/developerWorks/cn/
SOAP为在一个松散的、分布的环境中使用XML对等地交换结构化的和类型化的信息提供了一个简单的轻量级机制。而传送信息的基本单元承载体就是SOAP消息。从根本上来看,SOAP消息是从发送方到接受方的一种传输方法,但就象前面SOAP技术及应用概览一文中阐述的那样,SOAP消息一般会和实现模式结合,例如请求/响应。SOAP的实现可以为特殊网络系统的特有特征来优化。例如,通过HTTP binding将SOAP响应消息通过HTTP响应来传输,请求和响应使用同一连接。
当然,无论SOAP是与哪种协议绑定,它都使用同一种消息的描述框架格式,这种框架格式就是以SOAP Envelope(SOAP信封)为根元素,内含SOAP Header和SOAP Body子元素的这样一个XML文档。SOAP消息描述文本是一种XML Application。之所以称之为描述框架,而不称为描述,是因为SOAP规范为SOAP Header和SOAP Body定义了强大的扩展机制,使用户可以按需要在其中增加与应用相关的自定义描述格式,SOAP规范只是定义了消息描述的一个骨架。
所有的SOAP消息都是使用XML格式来编码的。SOAP应用程序在生成由SOAP定义的所有元素和属性的时候,应该包含恰当的SOAP的命名空间。SOAP应用程序必须能处理其收到的消息中的SOAP命名空间。它必须丢弃那些包含不正确命名空间的消息,并且可以处理那些不包含SOAP命名空间的SOAP消息,就好象他们包含了正确的命名空间一样。
SOAP定义了两个命名空间:
SOAP信封的命名空间标识为http://schemas.xmlsoap.org/soap/envelope/
SOAP编序的命名空间标识为http://schemas.xmlsoap.org/soap/encoding/
SOAP消息必须不包含DTD,同时SOAP消息也必须不包含PI(Processing Instructions)。
除SOAP mustUnderstand attribute和SOAP actor attribute外,一般允许属性及属性值自由地选择是在XML实例中描述还是在XML Schema中描述,当然前提是他们具有相同的效果。也就是说,在模式(schema)中使用默认值或固定值定义在语义上等价于在实例中的值定义。
例如:在Schema中定义
<element name=”company” type=”string” fixed=”DealEasy”>
然后在XML实例中定义
<company />
与直接在XML实例中定义
<company>DealEasy</company>
在语义上是等价的。
SOAP的消息框架
SOAP消息是由一个强制的SOAP Envelope、一个可选的SOAP Header和一个强制的SOAP Body组成的XML文档。作为SOAP消息的该XML文档将在本规范的其余部分被引用。而本节的元素和属性的命名空间标识是http://schemas.xmlsoap.org/soap/envelope/。SOAP消息应当包含如下部分:
§ Envelope是表示该消息的XML文档的顶级元素
§ Header则是为了支持在松散环境下在通讯方之间尚未预先达成一致的情况下为SOAP消息增加特性的通用机制。SOAP定义了很少的一些属性来用于指明谁可以处理该特性以及它是可选处理的还是强制处理的。
§ Body为该消息的最终接收者所想要得到的那些必须处理的信息提供了一个容器。此外,SOAP定义了Body的一个子元素Fault用于报告错误。
这些XML元素的语法规则如下:
1. Envelope
§ 元素名为 Envelope
§ 该元素必须在SOAP消息中出现,一般是根元素
§ 该元素可以包含命名空间申明和额外的属性。如果出现额外属性(并非是SOAP规范预定义的属性),则必须使用命名空间修饰。类似的,该元素可以包含额外的子元素,这些子元素如果出现,必须有命名空间修饰并且必须跟在SOAP Body元素之后,也就是说Envelope的直接子元素Header和Body必须排列在最前面。
2. Header
§ 元素名为 Header
§ 该元素可以在SOAP消息中出现,但并不是必须出现(也就是说可以仅使用Body元素完成一次SOAP消息的信息描述)。如果出现,该元素必须是SOAP Envelope元素的第一个直接子元素。
§ 该元素可以包含一系列的Header条目,这些条目都应当是Header元素的直接子元素。Header的所有直接子元素必须有命名空间修饰。
§ Header条目自身可以包含下级子元素,但这些元素不是Header条目,而是Header条目的内容。
3. Body
§ 元素名为 Body
§ 该元素必须在SOAP消息中出现,同时必须是SOAP Envelope元素的一个直接子元素。若该消息中包含Header元素,则Body元素必须直接跟随Header,为Header元素的相邻兄弟元素。若Header不出现,则其必须是Envelope的第一个直接子元素。
§ 该元素可以包含一系列的Body条目,这些条目都应当是Body元素的直接子元素。Body的所有直接子元素必须有命名空间修饰。SOAP定义了SOAP Fault元素,它用来指示调用错误的信息。
§ Boidy条目自身可以包含下级子元素,但这些元素不是Body条目,而是Body条目的内容。
下面是一个SOAP消息的例子,其中Envelope包含一个Header元素和一个Body元素。Header元素有两个Header条目,他们的命名空间修饰都是uniB2B,两个Header条目各有一个子元素。而Body元素有一个Body条目,该条目包含两个子元素。
<SOAP-ENV:Envelope
xmlns:SOAP-ENV=”http://schemas.xmlsoap.org/soap/envelope/”
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Header>
<uniB2B:AccessAuthenticated xmlns:uniB2B=”Some-URI”>
<SessionKey>76E4#12A@-98JA#V5GQ</SessionKey>
</uniB2B:AccessAuthenticated >
<uniB2B:GetLastProductPrice xmlns:uniB2B ="Some-URI">
<Price>243900.00</Price>
</uniB2B:GetLastProductPrice>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<uniB2B:RequestPurchaseOrder xmlns:uniB2B ="Some-URI">
<ProductID>Jaguar_X_Type</ProductID >
<ProductPrice>243900.00</ProductPrice>
</uniB2B:RequestPurchaseOrder >
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
SOAP encodingStyle属性
SOAP的全局encodingStyle属性被用于指明在SOAP消息中使用哪种编序规则。该属性可以在任意元素中出现,并且其作用范围包括该元素的内容和所有其子元素中未使用该属性的所有子元素,这就象XML命名空间定义的作用范围一样,是向下传递的。对于一个SOAP消息来说,没有默认的编码定义。
SOAP encodingStyle属性的值是一个或多个用于标识编序规则(对数据类型和数据类型实例的描述规则)和用于标识解序SOAP消息的规则的有序列表,其排序是按照详尽程度从大到小排列。下面是一些值的例子:
"http://schemas.xmlsoap.org/soap/encoding/"
"http://schema.dealeasy.com/soap/encoding/ http://schema.dealeasy.com/soap/encoding/additional/"
""
而SOAP规范中定义的编序规则的标识为http://schemas.xmlsoap.org/soap/encoding/。消息若要使用特别的编序应该使用SOAP encodingStyle属性来指明。另外,所有在句法上由http://schemas.xmlsoap.org/soap/encoding/开始的URI序列表明这其中包含的所有URI都与SOAP规范中定义的SOAP编码规则相一致。(虽然可能会添加潜在的更为严格的规则,也就是说http://schemas.xmlsoap.org/soap/encoding/标识的编码规则是一个编码的基类)
一个空值的URI(“”)明确地指明并未为其所包含的元素声明任何编码风格。这可以为包含的元素关闭任何前面预先的声明。
SOAP Header
SOAP提供了一个可扩展的机制用于在分散的网络环境下,模块化地扩展SOAP消息的描述能力,而通讯双方并不需要有完整的预先的约定。典型的扩展例子可以是实现一些诸如认证、事务管理以及支付的Header条目,当然对于更复杂的多步骤的B2B协同,通过扩展相对复杂的Header条目也可以实现,在前面的文章SOAP技术及应用概览中就有一个完整的例子。
按照SOAP的语法,Header元素应当被编码为SOAP Envelope XML文档的第一直接子元素。Header的所有直接子元素都被称为Header条目。
Header条目的编码规则包括:
1. 一个Header条目由一个完整修饰的元素名来标识,所谓完整修饰的元素名是由一个命名空间URI和局部名来组成。SOAP Header元素的所有直接子元素都必须是完整修饰的。不允许任何不带命名空间修饰的Header条目存在。
2. SOAP encodingStyle属性可以用于指明Header条目的编码风格,而encodingStyle属性在前面已经详细介绍了。
3. SOAP mustUnderstand属性和SOAP actor属性可以用于指明如何处理条目和由谁来处理条目。
在SOAP Header中,SOAP Header属性的设置是为了让SOAP消息的接收者了解应该如何处理该消息。一个生成SOAP消息的SOAP应用程序应该仅使用SOAP Header元素的直接子元素的SOAP Header属性。而对于那些并非作为SOAP Header元素的直接子元素出现的SOAP Header属性,SOAP消息的接受者必须忽略。
以下是一个Header的例子,其中包含了一个元素标识Priority和一个mustUnderstand属性及其值1,以及Priority的值7,该元素表明该消息的处理优先权为7。
<SOAP-ENV:Header>
<uniB2B:Priority
xmlns:uniB2B="some-URI" SOAP-ENV:mustUnderstand="1">
7
</uniB2B:Priority>
</SOAP-ENV:Header>
SOAP actor属性
SOAP消息从生成者到达最终接受者,将潜在地沿着消息路径(message path)经过一系列的SOAP中间介。SOAP中间介是一个能够接受和转发SOAP消息的应用程序。所有的中间介都如同最终接受者一样由一个URI来标识。
并非一个SOAP消息的所有部分都是最终接收者需要了解的调用信息,其中部分是路径中的一个或多个中间介所需要处理的。Header元素中接收者角色类似合约的接受者,他并不能将其交给其它方。也就是说,一个接收者接到其需要接收的Header元素必须不转发该Header给SOAP消息路径中的下一个应用程序。该接收者可以插入一个类似的Header元素,但在这个情况下,合约关系存在于该应用程序及下一个Header元素的接收者之间了。
SOAP actor全局属性可以被用于指明Header元素的接收者。而SOAP actor属性的值是一个URI。URI: http://schemas.xmlsoap.org/soap/actor/next指明该Header元素是直接的下一个进行消息处理的SOAP应用程序需要处理的。这与HTTP的连接头字段的hop-by-hop scope model的表示是一致的。
若省略SOAP actor属性,则表明该消息的接收者是SOAP消息的最终接收者。
对于一个SOAP消息的实例(在实际传输中的SOAP消息),这个属性必须出现以指明该消息的接收方的URI。
SOAP mustUnderstand属性
SOAP mustUnderstand全局属性用于指明一个Header条目是强制必须处理的还是可选的要求接收者处理的。Header条目的接收者由SOAP actor属性来定义。mustUnderstand属性的值可取为“0”或“1”。若没有使用SOAP mustUnderstand属性,则在语义上等价于mustUderstand属性出现同时取值为“0”。
若Header元素带有值为“1”的SOAP mustUnderstand属性,则该Header条目的接收者要么必须遵循语义(由具备完整修饰的元素名来传达)并正确地处理这些语义,要么必须宣称处理消息失败。也就是说如果该应用程序发现自己无法识别某一个Header条目(应为在自己的处理逻辑里面没有该条目),那么必须申明错误,并响应该错误信息。
SOAP mustUnderstand属性是为了考虑健壮地升级而设置的。所有用值为“1”的SOAP mustUnderstand属性来标记的元素必须被认为是可以影响该元素的上级元素或同级元素的语义。而这种风格标记的元素应保证对语义的修改并不能被那些不能完全理解该修改后的语义的那些元素静默地或假设地、不正确地忽略。
该属性若要生效必须在实例中出现,也就是说不能依靠XML Schema的缺省值或固定值的设置来使该属性生效。。
SOAP Body
SOAP Body元素提供一个简单的用于与消息的最终接收者交换信息(这些信息都是必须处理的)的机制。而Body元素的典型应用包含序列的RPC调用和错误报告。
Body元素在编码上应当作为SOAP Envelope元素的一个直接子元素。如果包含Header元素,则Body元素必须直接跟随Header元素,为Header元素的直接下一个兄弟元素,否则Body元素必须是Envelope元素的第一直接子元素。
所有Body元素的直接子元素被称为Body条目,同时每一个Body条目都应当编码为SOAP Body元素里的一个独立元素。
Body条目的编码规则包括:
1. 一个Body条目由一个完整修饰的元素名来标识,所谓完整修饰的元素名是由一个命名空间URI和局部名来组成。SOAP Body元素的直接子元素可以是命名空间修饰的。
2. SOAP encodingStyle属性可以被用来表明Body条目中使用的编码规则。
SOAP只预定义了一个Body条目:用于向调用方报告错误的Body条目:Fault。
SOAP Body的应用
下面是一对SOAP调用/响应的例子,在调用消息中Body包含了一个描述调用方法的Body条目RequestPurchaseOrder,它包含两个参数ProductID和ProductPrice。而在响应消息中,Body中包含了一个Body条目ResponsePurchaseOrder用于描述响应调用的返回信息,其中包含OrderID、AuthenticatedID、ProductID、ProductNumber、IssueDate。
RPC调用的SOAP消息表示
POST /ProductQuote HTTP/1.1
Host: service.eMarketplace.com.cn
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn
SOAPAction: "Some-URI"
<SOAP-ENV:Envelope
xmlns:SOAP-ENV=”http://schemas.xmlsoap.org/soap/envelope/”
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Header>
<uniB2B:AccessAuthenticated xmlns:uniB2B=”Some-URI”>
<SessionKey>76E4#12A@-98JA#V5GQ</SessionKey>
</uniB2B:AccessAuthenticated >
<uniB2B:GetLastProductPrice xmlns:uniB2B ="Some-URI">
<Price>243900.00</Price>
</uniB2B:GetLastProductPrice>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<uniB2B:RequestPurchaseOrder xmlns:uniB2B ="Some-URI">
<ProductID>Jaguar_X_Type</ProductID >
<ProductPrice>243900.00</ProductPrice>
</uniB2B:RequestPurchaseOrder >
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
RPC响应的SOAP消息表示
HTTP/1.1 200 OK
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn
<SOAP-ENV:Envelope
xmlns:SOAP-ENV=”http://schemas.xmlsoap.org/soap/envelope/”
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<uniB2B:ResponsePurchaseOrder xmlns:uniB2B ="Some-URI">
<OrderID>AJR786503</OrderID>
<AuthenticatedID>76E4#12A@-98JA#V5GQ</AuthenticatedID>
<ProductID>Jaguar_X_Type</ProductID >
<ProductPrice>243900.00</ProductPrice>
<ProductNumber>1</ProductNumber>
<IssueDate>2001-4-1</IssueDate>
</uniB2B:ResponsePurchaseOrder >
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
SOAP Header和SOAP Body的语义关系
Header和Body在定义上是独立的,但在事实上是相联系的。一个Body条目和一个Header条目的关系是:一个Body条目在语义上与这样一个Header条目等价:该Header条目将由默认参与者(最终接受者)解释同时由值为“1”的SOAP mustUnderstand属性标记。默认参与者可以不使用actor属性的方式来指明。
SOAP Fault
SOAP Fault元素是用于在SOAP消息中传输错误及状态信息。如果SOAP消息需要包含SOAP Fault元素的话,它必须作为一个Body条目出现,同时在Body元素内它必须不出现多于一次(至多出现一次)。
SOAP Fault元素定义了如下子元素:
faultcode
faultcode元素是应那些要以算法的机制来标识错误的软件的需要。faultcode必须在SOAP Fault元素中出现,同时faultcode的值必须是属于后面一节中定义的一个修饰名。SOAP定义了一个很小的SOAP错误代码的集合用于覆盖基本的SOAP错误。
faultstring
faultstring元素是为那些错误代码提供人可以读懂的错误解释,它不是为程序处理而设。Faultstring元素有点类似于HTTP中定义的’Reason-Phrase’。faultstring必须在SOAP Fault元素中出现,同时它至少应该提供一些解释该错误种类的信息。
faultactor
faultactor元素是为在消息路径中是谁引起了该错误的发生这一情况描述信息。它类似于SOAP actor属性,不过它不是用于指示Header条目的接收者,而是用于指示错误源。faultactor属性的值是一个标识该源的一个URI。作为SOAP消息的非最终接收者的应用程序必须在SOAP Fault元素中包含faultactor元素。而消息的最终接收者可以使用faultactor元素来明确地指明是它生成了该错误。
detail
detail元素是用于传输与Body元素相关的应用程序特别的错误信息。如果Body元素中的内容不能被成功地处理的时候,它必须出现。它必须不能被用于传输属于Header条目的错误信息。详细的属于Header条目的错误信息必须在Header条目中表示传输。
若Fault元素中不出现detail元素则表明其中的错误与Body元素的处理无关。这可以用于区分在在错误情况下Body元素是否被处理过。
detail元素的所有直接子元素都被称为detail条目,同时每个detail条目都作为detail元素中的一个独立的元素进行编码。
Detail条目的编码规则如下:
1. 一个detail条目由一个完整修饰的元素名来标识,所谓完整修饰的元素名是由一个命名空间URI和局部名来组成。Detail元素的直接子元素可以是命名空间修饰的。
2. SOAP encodingStyle属性可以被用来表明detail条目中使用的编码规则。
其他的Fault子元素可以出现,他们都应当提供命名空间修饰。
SOAP Fault Codes
当描述由本规范定义的错误的时候,faultcode元素必须使用在本节中定义的faultode的值。这些faultcode值的命名空间标识为http://schemas.xmlsoap.org/soap/envelope/。
默认的SOAP faultcode值是按照一种可扩展的风格来定义的,它允许在维持以有的faultcode值的向后兼容的基础定义新的SOAP faultcode值。这一机制在使用上非常类似与HTTP中基本状态类的定义1xx, 2xx, 3xx等。不过,他们是用XML修饰名来定义,而不是用整数。“.”符号是faultcode值的分隔符,用于指明“.”左边的是一个比右边更泛化的错误代码。例如:
Client.Authentication
在本文档中定义的faultcode值集合是:
Name Meaning
VersionMismatch 处理程序发现在SOAP Envelope元素中有一个非法的命名空间。
MustUnderstand SOAP Header元素的一个直接子元素无法被理解或者他并不遵守由处理对象要求的SOAP mustUnderstand属性必须取值为“1”的要求。
Client Client错误类用于指示以下错误:消息的格式有误或消息中缺乏能成功处理所必须的一些适当信息。例如,消息中可能缺乏适当的认证和支付信息。一般情况下应指明消息不应该在没有修改过的情况下重发。可参阅Fault detail子元素的描述。
Server Server错误类用于表明消息无法被处理的原因,但那些属于内容上的错误并不属于该范畴,它主要被用于指示那些属于处理上的错误。例如,处理操作需要包含与一个上游处理程序进行通讯,但该程序没有响应。但该消息可能在下一个时间点上被成功处理。可参阅Fault detail子元素的描述。