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 中定义
1<element fixed="”DealEasy”" name="”company”" type="”string”">
2
3然后在 XML 实例中定义
4
5<company></company>
6
7与直接在 XML 实例中定义
8
9<company>DealEasy</company>
10
11在语义上是等价的。
12
13## SOAP 的消息框架
14
15SOAP 消息是由一个强制的 SOAP Envelope 、一个可选的 SOAP Header 和一个强制的 SOAP Body 组成的 XML 文档。作为 SOAP 消息的该 XML 文档将在本规范的其余部分被引用。而本节的元素和属性的命名空间标识是 http://schemas.xmlsoap.org/soap/envelope/ 。 SOAP 消息应当包含如下部分:
16
17§ Envelope 是表示该消息的 XML 文档的顶级元素
18
19§ Header 则是为了支持在松散环境下在通讯方之间尚未预先达成一致的情况下为 SOAP 消息增加特性的通用机制。 SOAP 定义了很少的一些属性来用于指明谁可以处理该特性以及它是可选处理的还是强制处理的。
20
21§ Body 为该消息的最终接收者所想要得到的那些必须处理的信息提供了一个容器。此外, SOAP 定义了 Body 的一个子元素 Fault 用于报告错误。
22
23这些 XML 元素的语法规则如下:
24
251\. Envelope
26
27§ 元素名为 Envelope
28
29§ 该元素必须在 SOAP 消息中出现,一般是根元素
30
31§ 该元素可以包含命名空间申明和额外的属性。如果出现额外属性 ( 并非是 SOAP 规范预定义的属性 ) ,则必须使用命名空间修饰。类似的,该元素可以包含额外的子元素,这些子元素如果出现,必须有命名空间修饰并且必须跟在 SOAP Body 元素之后,也就是说 Envelope 的直接子元素 Header 和 Body 必须排列在最前面。
32
332\. Header
34
35§ 元素名为 Header
36
37§ 该元素可以在 SOAP 消息中出现,但并不是必须出现 ( 也就是说可以仅使用 Body 元素完成一次 SOAP 消息的信息描述 ) 。如果出现,该元素必须是 SOAP Envelope 元素的第一个直接子元素。
38
39§ 该元素可以包含一系列的 Header 条目,这些条目都应当是 Header 元素的直接子元素。 Header 的所有直接子元素必须有命名空间修饰。
40
41§ Header 条目自身可以包含下级子元素,但这些元素不是 Header 条目,而是 Header 条目的内容。
42
433\. Body
44
45§ 元素名为 Body
46
47§ 该元素必须在 SOAP 消息中出现,同时必须是 SOAP Envelope 元素的一个直接子元素。若该消息中包含 Header 元素,则 Body 元素必须直接跟随 Header ,为 Header 元素的相邻兄弟元素。若 Header 不出现,则其必须是 Envelope 的第一个直接子元素。
48
49§ 该元素可以包含一系列的 Body 条目,这些条目都应当是 Body 元素的直接子元素。 Body 的所有直接子元素必须有命名空间修饰。 SOAP 定义了 SOAP Fault 元素,它用来指示调用错误的信息。
50
51§ Boidy 条目自身可以包含下级子元素,但这些元素不是 Body 条目,而是 Body 条目的内容。
52
53下面是一个 SOAP 消息的例子,其中 Envelope 包含一个 Header 元素和一个 Body 元素。 Header 元素有两个 Header 条目,他们的命名空间修饰都是 uniB2B ,两个 Header 条目各有一个子元素。而 Body 元素有一个 Body 条目,该条目包含两个子元素。
54
55<soap-env:envelope envelope="" http:="" schemas.xmlsoap.org="" soap="" soap-env:encodingstyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soap-env="”" ”="">
56<soap-env:header>
57<unib2b:accessauthenticated xmlns:unib2b="”Some-URI”">
58<sessionkey>76E4#12A@-98JA#V5GQ</sessionkey>
59</unib2b:accessauthenticated>
60<unib2b:getlastproductprice xmlns:unib2b="Some-URI">
61<price>243900.00</price>
62</unib2b:getlastproductprice>
63</soap-env:header>
64<soap-env:body>
65<unib2b:requestpurchaseorder xmlns:unib2b="Some-URI">
66<productid>Jaguar_X_Type</productid>
67<productprice>243900.00</productprice>
68</unib2b:requestpurchaseorder>
69</soap-env:body>
70</soap-env:envelope>
71
72## SOAP encodingStyle 属性
73
74SOAP 的全局 encodingStyle 属性被用于指明在 SOAP 消息中使用哪种编序规则。该属性可以在任意元素中出现,并且其作用范围包括该元素的内容和所有其子元素中未使用该属性的所有子元素,这就象 XML 命名空间定义的作用范围一样,是向下传递的。对于一个 SOAP 消息来说,没有默认的编码定义。
75
76SOAP encodingStyle 属性的值是一个或多个用于标识编序规则 ( 对数据类型和数据类型实例的描述规则 ) 和用于标识解序 SOAP 消息的规则的有序列表,其排序是按照详尽程度从大到小排列。下面是一些值的例子:
77
78"http://schemas.xmlsoap.org/soap/encoding/"
79"http://schema.dealeasy.com/soap/encoding/ http://schema.dealeasy.com/soap/encoding/additional/"
80""
81
82而 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/ 标识的编码规则是一个编码的基类 )
83
84一个空值的 URI( “” ) 明确地指明并未为其所包含的元素声明任何编码风格。这可以为包含的元素关闭任何前面预先的声明。
85
86## SOAP Header
87
88SOAP 提供了一个可扩展的机制用于在分散的网络环境下,模块化地扩展 SOAP 消息的描述能力,而通讯双方并不需要有完整的预先的约定。典型的扩展例子可以是实现一些诸如认证、事务管理以及支付的 Header 条目,当然对于更复杂的多步骤的 B2B 协同,通过扩展相对复杂的 Header 条目也可以实现,在前面的文章 ** SOAP ** ** 技术及应用概览 ** 中就有一个完整的例子。
89
90按照 SOAP 的语法, Header 元素应当被编码为 SOAP Envelope XML 文档的第一直接子元素。 Header 的所有直接子元素都被称为 Header 条目。
91
92Header 条目的编码规则包括:
93
941\. 一个 Header 条目由一个完整修饰的元素名来标识,所谓完整修饰的元素名是由一个命名空间 URI 和局部名来组成。 SOAP Header 元素的所有直接子元素都必须是完整修饰的。不允许任何不带命名空间修饰的 Header 条目存在。
95
962\. SOAP encodingStyle 属性可以用于指明 Header 条目的编码风格,而 encodingStyle 属性在前面已经详细介绍了。
97
983\. SOAP mustUnderstand 属性和 SOAP actor 属性可以用于指明如何处理条目和由谁来处理条目。
99
100在 SOAP Header 中, SOAP Header 属性的设置是为了让 SOAP 消息的接收者了解应该如何处理该消息。一个生成 SOAP 消息的 SOAP 应用程序应该仅使用 SOAP Header 元素的直接子元素的 SOAP Header 属性。而对于那些并非作为 SOAP Header 元素的直接子元素出现的 SOAP Header 属性, SOAP 消息的接受者必须忽略。
101
102以下是一个 Header 的例子,其中包含了一个元素标识 Priority 和一个 mustUnderstand 属性及其值 1 ,以及 Priority 的值 7 ,该元素表明该消息的处理优先权为 7 。
103
104<soap-env:header>
105<unib2b:priority soap-env:mustunderstand="1" xmlns:unib2b="some-URI">
1067
107</unib2b:priority>
108</soap-env:header>
109
110## SOAP actor 属性
111
112SOAP 消息从生成者到达最终接受者,将潜在地沿着消息路径 (message path) 经过一系列的 SOAP 中间介。 SOAP 中间介是一个能够接受和转发 SOAP 消息的应用程序。所有的中间介都如同最终接受者一样由一个 URI 来标识。
113
114并非一个 SOAP 消息的所有部分都是最终接收者需要了解的调用信息,其中部分是路径中的一个或多个中间介所需要处理的。 Header 元素中接收者角色类似合约的接受者,他并不能将其交给其它方。也就是说,一个接收者接到其需要接收的 Header 元素必须不转发该 Header 给 SOAP 消息路径中的下一个应用程序。该接收者可以插入一个类似的 Header 元素,但在这个情况下,合约关系存在于该应用程序及下一个 Header 元素的接收者之间了。
115
116SOAP actor 全局属性可以被用于指明 Header 元素的接收者。而 SOAP actor 属性的值是一个 URI 。 URI: http://schemas.xmlsoap.org/soap/actor/next 指明该 Header 元素是直接的下一个进行消息处理的 SOAP 应用程序需要处理的。这与 HTTP 的连接头字段的 hop-by-hop scope model 的表示是一致的。
117
118若省略 SOAP actor 属性,则表明该消息的接收者是 SOAP 消息的最终接收者。
119
120** 对于一个 SOAP ** ** 消息的实例 ( ** ** 在实际传输中的 SOAP ** ** 消息 ) ** ** ,这个属性必须出现以指明该消息的接收方的 URI ** ** 。 **
121
122## SOAP mustUnderstand 属性
123
124SOAP mustUnderstand 全局属性用于指明一个 Header 条目是强制必须处理的还是可选的要求接收者处理的。 Header 条目的接收者由 SOAP actor 属性来定义。 mustUnderstand 属性的值可取为“ 0 ”或“ 1 ”。若没有使用 SOAP mustUnderstand 属性,则在语义上等价于 mustUderstand 属性出现同时取值为“ 0 ”。
125
126若 Header 元素带有值为“ 1 ”的 SOAP mustUnderstand 属性,则该 Header 条目的接收者要么必须遵循语义 ( 由具备完整修饰的元素名来传达 ) 并正确地处理这些语义,要么必须宣称处理消息失败。也就是说如果该应用程序发现自己无法识别某一个 Header 条目 ( 应为在自己的处理逻辑里面没有该条目 ) ,那么必须申明错误,并响应该错误信息。
127
128SOAP mustUnderstand 属性是为了考虑健壮地升级而设置的。所有用值为“ 1 ”的 SOAP mustUnderstand 属性来标记的元素必须被认为是可以影响该元素的上级元素或同级元素的语义。而这种风格标记的元素应保证对语义的修改并不能被那些不能完全理解该修改后的语义的那些元素静默地或假设地、不正确地忽略。
129
130** 该属性若要生效必须在实例中出现,也就是说不能依靠 XML Schema ** ** 的缺省值或固定值的设置来使该属性生效。。 **
131
132## SOAP Body
133
134SOAP Body 元素提供一个简单的用于与消息的最终接收者交换信息 ( 这些信息都是必须处理的 ) 的机制。而 Body 元素的典型应用包含序列的 RPC 调用和错误报告。
135
136Body 元素在编码上应当作为 SOAP Envelope 元素的一个直接子元素。如果包含 Header 元素,则 Body 元素必须直接跟随 Header 元素,为 Header 元素的直接下一个兄弟元素,否则 Body 元素必须是 Envelope 元素的第一直接子元素。
137
138所有 Body 元素的直接子元素被称为 Body 条目,同时每一个 Body 条目都应当编码为 SOAP Body 元素里的一个独立元素。
139
140Body 条目的编码规则包括:
141
1421\. 一个 Body 条目由一个完整修饰的元素名来标识,所谓完整修饰的元素名是由一个命名空间 URI 和局部名来组成。 SOAP Body 元素的直接子元素可以是命名空间修饰的。
143
1442\. SOAP encodingStyle 属性可以被用来表明 Body 条目中使用的编码规则。
145
146SOAP 只预定义了一个 Body 条目:用于向调用方报告错误的 Body 条目 :Fault 。
147
148## SOAP Body 的应用
149
150下面是一对 SOAP 调用 / 响应的例子,在调用消息中 Body 包含了一个描述调用方法的 Body 条目 RequestPurchaseOrder ,它包含两个参数 ProductID <SPAN style="FONT-FAMILY: 宋体;</element>