● 了解J2EE Web Services 概念 1: J2EE Web 服务是关于互操作性的,要掌握的最重要的事情就是互操作性了。 2 这些核心是由:WS-I(Web Services Interoperability Organization Web 服务互操作性组织)发布的 Basic Profile1.0(简称BP) 3 Bp提供了一套用于确定应用程序如何使用公共Web服务技术的规则。 4 Web 服务是一个符合Web Services互操作性组织的Basic Profile1.0的软件应用程序 【必读】 Web服务互操作组织WS-I是一个Web服务供应商的组织。这些供应商要给Web服务互操作性定义一个标准。WS-I的第一个可使用标准是Basic Profile 1.0(BP)。它详细的说明了如何在Web服务中 一起使用4个主流的Web服务规范。BP定义了一些规则,来澄清XML,SOAP,WSDL和UDDI规范中的模糊部分,并定义了如何使用这些技术来注册,描述Web服务以及如何与Web服务通信。 BP是必不可少的,因为各主要规范涉及的范围太广泛,不能对给定的互操作性给予说明。例如:WSDL是一个非常通用的技术,它允许用户描述各种类型的Web服务。遗憾的是,由于WSDL太通用了 ,所以在某些场合下难以准确的确定如何格式化要于WEB服务交换的SOAP消息。而BP通过准确的说明WSDL如何描述基于SOAP的web服务并通过限制WSDL的使用解决了互操作性问题。 ● SOAP 1 其实Web Services 的核心就是SOAP和WSDL,它们隐含于J2EE Web Services平台的通信层于部署层 2 什么是SOAP SOAP最初是简单对象访问协议(即Simple Object Access Propotol)的缩写,但它只是个名称而已。SOAP1.1是J2EE Web Services使用的标准消息传递协议,而且通常是Web服务的事实标准。 SOAP的主要应用是应用程序与应用程序之间的通信(A2A),且主要应用于商务对商务(B2B)的通信以及企业应用集成(EAI)。B2B和A2A是同一个问题的2个方面,两者都要解决集成软件应用程序以 及共享数据这样的问题。为了使B2B和EAI真正起作用,协议必须是与平台无关,具有灵活性并且基于标准且通用的技术。 SOAP技术与早期的B2B和EAI技术,如CORBA和EDI(电子文档交换技术)不一样,SOAP能满足这些要求,而且应用广泛,并已得到大多软件企业的支持,如(W3C,WS-I,OASIS)。 其实SOAP只不过是伴随其使用规则的另一个XML标记语言。SOAP有一个清晰的目标,即通过网络交换数据。它将重点放在了如何封装,编码XML数据以及如何定义用于传输和接收这些数据的规则 上了。简单的说,SOAP就是一个网络应用协议。 5为什么SOAP会非常受欢迎 SOAP定义了如何通过软件以独立于各种编程语言或平台的方式来构造消息,处理消息,从而使那些用于不用编程语言编写的程序之间具有互操作性,并能在不同的操作系统上运行。 它通过XML命名空间使SOAP具有更强的扩展性。而且它基于XML为基础,并且SOAP它用于HTTP隧道的标准方法。HTTP隧道是将另外一个协议隐藏与HTTP消息中以穿越未收到阻止的防火墙的过程 。防火墙通常允许HTTP通过端口80通行,但会禁止或限制使用其他协议或端口。 5 SOAP的基本结构 <?xml version=”1.0” encoding=”UTF-8” ?> <soap:Envelope Xmlns:soap=http://schemas.xmlsoap.org/soap/envelope/ > <soap:Body> ………………… ………………………… …………………. </soap:Body> </soap:Envelope> SOAP消息与传统邮递服务中使用的信封类似。就像纸信封内包含信件一样,SOAP消息包含了XML数据。 SOAP中根元素为Envelope元素。 Envelope元素可以包含余个可选的Header元素,同时必须包含一个Body元素。 如果用户使用了Header元素,那么该元素必须是Envelope元素的直接子元素。并在Body元素之前。 Header元素以一个或多个不同XML元素的形式包含消息方面的信息,其中每一个元素描述与消息关联的服务的某些方面或质量(服务质量是说-------) Header元素可以包含各种XML元素,这些XML元素用于描述安全凭证,ID,路由指令,或在Body元素中处理数据时涉及的其他非常重要的消息方面的信息。可以这样说,如果我们希望为每一个SOAP消息 附加一个唯一的标识符,用于测试和登陆。虽然唯一标识符不是SOAP协议本身的部分,但用户可以容易的向Herder元素添加标识符,如下例: <?xml version = “1.0” encoding = “UTF-8” ?> <soap:Envelope Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope” Xmlns:mi=”http://www.Monson-Haefel.com/jwsbook/message-id “ > <soap:Header> <mi:message-id>2223322332233232:fffff</mi:message> </soap:Header> <soap:Body> <!-- --> </soap:Body> </soap:Envelope> 值得注意的是:message-id元素称为文件头,并且该元素是由其自己的命名空间标识的任意XML元素。 又如下面例子,带XML数字签名文件头的SOAP消息 : <?xml version = “1.0” encoding = “UTF-8” ?> <soap:Envelope> Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope” Xmlns:mi=”http://www.Monson-Haefel.com/jwsbook/message-id “ Xmlns:sec=http://schemas.xmlsoap.org/soap/security/2000-12 Xmlns:ds=http://w3c.org/2000/09/xmldsig#> <soap:Header> <mi:message-id>111111111111111111111111111111111:-8000</mi:message> <sec:Signature> <ds:signature> ………………… ………………. </ds:signature> </sec:Signature> </soap:Header> </soap:Envelope> 也就是说,可以在Header元素中放入任意数量的文件头,而且每个代码快均由合适的对应的函数来处理。。。。 6 SOAP命名空间 XML命名空间在SOAP里起着非常重要的作用。因为SOAP可以在Header和Body元素中包含若干不同的XML元素,为了避免冲突,必须使用唯一的命名空间来标识它们。 如下例: : <?xml version = “1.0” encoding = “UTF-8” ?> <soap:Envelope> Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope” Xmlns:mi=”http://www.Monson-Haefel.com/jwsbook/message-id “ Xmlns:sec=http://schemas.xmlsoap.org/soap/security/2000-12 Xmlns:ds=http://w3c.org/2000/09/xmldsig#> <soap:Header> <mi:message-id>111111111111111111111111111111111:-8000</mi:message> <sec:Signature> <ds:signature> </ds:signature> </sec:Signature> </soap:Header> <soap:Body sec:id=”Body”> <po:purchaseOrder orderDate=”2003-09-22” Xmlns:po = http://www.Monson-Haefel.com/jwsbook/po Xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance> …………………………………………… </po:purchaseOrder> </soap:Body> </soap:Envelope> 正是因为SOAP使用了XML命名空间,所以使SOAP成为灵活且可扩展的协议。。命名空间完全限定了元素名称或属性名称。。 【注意】在Envelope元素声明的第一个命名空间定义了标准SOAP元素(即Envelop,Header,和Body元素)的命名空间。。。。如: <soap:Envelope> <Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope”> </soap:Envelope> 此命名空间确定了所使用的SOAP1.1版本,SOAP消息必须使用Envelope元素的命名空间声明成如下标准的SOAP1.1信封命名空间: <xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope”> 此规则保证了所有确认消息使用完全相同的命名空间和XML模式。。。 Header元素中的每个文件头应有自己的命名空间,这一点非常重要,因为命名空间有帮助SOAP应用程序指定文件头并单独处理它们 SOAP消息的所有本地元素都必须是命名空间限定的元素(就是SOAP1.1命名空间为前缀),因为SOAP1.1的XML模式将ElementFormDefault属性设置为了”qualified”,此外,Basic profile1.0要求所有由 Body元素包含的应用程序专用的元素必须喂限定元素。 同样可以通过声明xsi:schemaLocation属性来进行有效性检验,但大多数情况下,SOAP堆栈会在设计时处理此问题,并不需要在SOAP消息中显示的声明xsi: schemaLocation。 【解释】所谓的SOAP堆栈使用于处理和传递SOAP消息的代码库。比如,J2EE1.4,Microsoft.NET都用自己的SOAP堆栈,即用于处理SOAP消息的自己的代码库。 【补充】有些SOAP堆栈要大量利用XML模式实例命名空间来说明元素的数据类型(比如:xsi:type=”xsd:float”),可是有些SOAP则不一样。当接收方需要类型化元素但发送方没有类型化元素时,这样的 堆栈就会出问题。根据BP,只能用xsi:type属性来表示正在派生的XML类型替换它的基本数据, 对SOAP和应用程序专用的数据使用完全限定的名称会告诉SOAP接收方如何处理消息,以及要采用哪些XML模式来验证内容。。如: ① 文件头中的具体版本差异会影响接受方处理处理消息的方式,所以通过其命名空间指定的文件头版本可以使接收方切换处理模块,或当它不支持的时候拒绝消息。 ② 合理的确定Body元素中包含的XML元素的类型可以使SOAP接收方用对应的代码模块处理这些元素,或者当它不支持指定的额命名空间时可能会拒绝消息。 【总结】 可以看出,SOAP消息可以包含许多不同的命名空间,从而使SOAP消息传递更具模块化,此模块化使得SOAP消息的不同部分能够独立于其他部分加以处理,并且能单独进行更改。 也就是说,我们可以不断更改SOAP Envelope或文件头的版本。但Body元素中应用程序专用内容的结构还是保持不变。 【注意】 SOAP消息传递模式的模块化也允许处理SOAP消息的代码模块化 ① 处理Envelope元素的代码独立于处理文件头的代码 ② 而文件头独立于SOAP Body 元素中处理应用程序专用的数据代码。 ③ 模块化使用户能够用不同的代码库来处理SOAP消息的不同部分。 7 SOAP消息命名空间的特殊性 SOAP消息的命名空间目前为止都是在Envelope中,但并非一定可以这样做。 可以在Header元素或在文件头中声明这些命名空间。命名空间总是局部地确定范围,而且可以在任何级别加以声明,只要讨论的问题位于该范围内即可。 如下例: 在Header元素中声明命名空间 <?xml version = “1.0” encoding = “UTF-8” ?> <soap:Envelope> <soap:Header Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope” Xmlns:mi=”http://www.Monson-Haefel.com/jwsbook/message-id “ Xmlns:sec=http://schemas.xmlsoap.org/soap/security/2000-12 Xmlns:ds=http://w3c.org/2000/09/xmldsig#> <mi:message-id>111111111111111111111111111111111:-8000</mi:message> <sec:Signature> <ds:signature> </ds:signature> </sec:Signature> </soap:Header> <soap:Body sec:id=”Body”> <po:purchaseOrder orderDate=”2003-09-22” …………………………………………… </po:purchaseOrder> </soap:Body> </soap:Envelope> 在文件头中声明命名空间 <?xml version = “1.0” encoding = “UTF-8” ?> <soap:Envelope> <soap:Header> <mi:message-id>111111111111111111111111111111111:-8000</mi:message> <sec:Signature> <ds:signature Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope” Xmlns:mi=”http://www.Monson-Haefel.com/jwsbook/message-id “ Xmlns:sec=http://schemas.xmlsoap.org/soap/security/2000-12 Xmlns:ds=http://w3c.org/2000/09/xmldsig# > </ds:signature> </sec:Signature> </soap:Header> <soap:Body sec:id=”Body”> <po:purchaseOrder orderDate=”2003-09-22” …………………………………………… </po:purchaseOrder> </soap:Body> </soap:Envelope> 这里在补充一下的是,虽然要求用前缀来限定Body元素中的应用程序专用的元素,但对包含在Header元素中的元素没有这样额要求。。 下表是WS-I Basic Profile 1.0中使用的命名空间前缀: 前缀 命名空间 Soap “http://schemas.xmlsoap.org/soap/envelope” Xsi “http://www.w3.org/2001/XMLSchema-instance” Xsd “http://www.w3.org/2001/XMLSchema” Soapenc “http://schemas.xmlsoap.org/soap/encoding/” Wsdl “http://schemas.xmlsoap.org/wsdl/” Soapbind “http://schemas.xmlsoap.org/wsdl/soap/” Wsi “http://ws-i.org/schema/conformanceClaim/” SOAP头 首先要说明的是SOAP规范(制定者是Microsoft): 1 SOAP信封: 请求,响应,标识所要应用程序功能点的消息结构。 2 SOAP编码方式: 序列化SOAP消息和这些消息中的应用程序定义类型的标准机制 3 SOAP RPC表示: 表示远程过程调用的机制 而SOAP头规范定义了一些规则,在消息路径中必须根据这些规则来处理文件头。而消息路径则是SOAP消息从初始发送方到最终接收方的路由。SOAP规则要指定哪些节点必须处理特殊的文件头,以及对 文件头进行处理后应该做什么方面的事。 SOAP术语介绍: SOAP与其他应用程序协议不同,SOAP并不显至于单个的消息传递范例,SOAP可以与多种消息传递系统(如同步,异步,RPC,单向等)一起使用,可以用非传统的方式组合它们。而SOAP是用于通过网 络在SOAP应用程序间交换消息的协议。所以可以这样理解: SOAP应用程序只不过是一个用于产生或处理SOAP消息的软件,发送SOAP消息的应用程序称为发送方。接收SOAP消息的应用程序称为接收方。而这些应用程序有时候同时具备这2个功能。 SOAP消息沿消息路径从发送方传递到接收方,所有的SOAP消息起始于初始发送方,并在最终接收方结束。客户这一术语有时指请求消息的初始发送方,而术语Web服务则指请求消息的最终接收方。 消息传递以及运用的概念 当SOAP消息沿消息路径传递时,其头文件可以由任意数量的SOAP中介体解释与处理。 SOAP中介体既可以是接收方也可以使发送方,,它可以接收SOAP消息,处理一个或多个文件头,并将SOAP消息传送到另外一个SOAP应用程序,沿消息路径(初始发送方,中介体,最终接收方)的 各应用程序也称为SOAP节点 我希望通过一个例子来解释消息路径中的节点如何处理文件头。我们假设有2个相对简单的文件头Message-id和Processed-by。文件头Message-id用于记录SOAP应用程序(即节点),这些SOAP应用程序 要沿着从初始发送方到最终接收方这一传递路径处理SOAP消息。 与Message-id文件头类似,Processed-by文件头用于调试程序与记录。 下例中,SOAP消息在到达最终接收方之前要通过若干个中接替传递。我来说说中介体以等的概念吧: 假设SOAP消息的初始发送方是一个客户节点,最终接收方是一个运输节点,那么中间会经过一些必要的途径,比如:销售节点,应付款节点,库存节点,最后到运输节点。 从初始发送放到最终接受方中间所经过的一系列的节点,我们称为中介体。它们也会传送SOAP消息到下一个中介体。 还要在强调一个概念的是:位于SOAP消息路径中的各个中介体不能修改SOAP Body元素中的应用程序专用内容,但可以处理SOAP头文件,而且经常这么做。 在下面的例子中,要求每个SOAP中介体向Processed-by 文件头添加一个Note元素,以标识其本身并确定其处理消息的时间。程序在五个应用程序均向Processed-by文件头添加Node元素之后的消息。 <?xml version = “1.0” encoding = “UTF-8” ?> <soap:Envelope> Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope” Xmlns:mi=”http://www.Monson-Haefel.com/jwsbook/message-id “ Xmlns:sec=http://schemas.xmlsoap.org/soap/security/2000-12 Xmlns:ds=http://w3c.org/2000/09/xmldsig# Xmlns:proc=”http://www.Monson-Haefel.com/jwsbook/processed-by”> <soap:Header> <proc:processed-by> <!—下面出现了5个节点。分别向Processed-by添加Node元素之后的消息--> <node> <time-in-millis>10136123146231</time-in-millis> <identity>http://www.customer.com</identity> </node> <node> <time-in-millis>101345416231</time-in-millis> <identity>http://www.Monson-Haefel.com </identity> </node> <node> <time-in-millis>1035345234231</time-in-millis> <identity> http://www.Monson-Haefel.com </identity> </node> <node> <time-in-millis>156564346231</time-in-millis> <identity> http://www.Monson-Haefel.com </identity> </node> <node> <time-in-millis>103423454</time-in-millis> <identity> http://www.Monson-Haefel.com </identity> </node> </proc:processed-by> </soap:Header> <soap:Body> <!—Application –Specific data goes Here--> </soap:Body> </soap:Envelope> 此例,当处理文件头时,每个节点在将SOAP消息传递到下一个接收方之前进行从SOAP消息读文件头,对文件头进行处理,然后删除文件头这样的操作。但消息路径中的节点如何知道它要处理哪一个文 件头呢? SOAP1.1应用程序用actor属性来识别用于处理指定文件头的节点。还用mustUnderstand属性表示处理头的节点是否能够识别文件头以及知道如何处理它。 Actor属性 Actor属性由SOAP1.1注释定义,它是SOAP的Envelope,Body,和Header元素的命名空间(即Http://schemas,xmlsoap.org/soap/envelop/)的一部分。 我们可以使用Actor属性指定具体节点所完成的功能、 Actor属性使用一个URI标识节点处理对应的文件头时必须扮演的角色。当一个节点接收到一个SOAP消息时,它要分析每一个文件头,以确定哪些代码块是由该节点所支持的加色(我在这里说一下,其实 Actor这个属性就可以看成一个人,他在这个社会中会扮演很多的角色,比如:父亲 儿子 丈夫 医生,同样,Actor也是如此) Actor属性与XML命名空间组合在一起使用,以确定要用哪一个代码模块处理具体的文件头。因此,接收节点首先要根据文件头的命名空间确定它是否起Actor属性所指定的角色。 然后选择对应的代码模块来处理这些文件头。。。 所以,接收节点必须承认由赋予文件头的Actor属性所指定的角色,同时还要承认与文件头对应的XML命名空间。。 一个节点可以由对一个消息进行操作的多个模块,所以它会由多个角色。 这样的话,我们可以使用若干个不同的角色来标识消息路径中的每一个节点。 如下例:() 1-1例 <?xml version = “1.0” encoding = “UTF-8” ?> <soap:Envelope> Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope” Xmlns:mi=”http://www.Monson-Haefel.com/jwsbook/message-id “ Xmlns:sec=http://schemas.xmlsoap.org/soap/security/2000-12 Xmlns:ds=http://w3c.org/2000/09/xmldsig# Xmlns:proc=”http://www.Monson-Haefel.com/jwsbook/processed-by”> <soap:Header> <mi:message-id soap:actor=”http://www.Monson-Haefel.com/logger”> 111111111111111111111111111111111:-8000 </mi:message> <sec:Signature> <ds:signature> </ds:signature> </sec:Signature> </soap:Header> <soap:Body sec:id=”Body”> <po:purchaseOrder orderDate=”2003-09-22” …………………………………………… </po:purchaseOrder> </soap:Body> </soap:Envelope> 此例子,在消息路径中,只有用了Actor属性值http://www.Monson-Haefel.com/logger标识了的节点才能处理Message-id文件头,其他节点会忽略该文件头。 【注意】除了像Http://www.Monson-Haefel.com/logger这样的客户URI之外,SOAP还为actor属性标识了2个标准角色,即Next和Ultimate receiver角色,这两个标准角色标识应该处理文件头的各节点,而 且它们具有较好的自释性 Next 和 Ultimate receiver 角色 Next角色表示消息路径中的下一个节点必须处理文件头。Next角色有一个指定的URI,即http://schemas.xmlsoap.org/soap/actor/next,此URI必须作为actor属性的值 Ultimate receiver角色表示只有消息的最终接受方才能处理文件头。协议并没有为此提供明显的URI。。 如果某一角色在文件头中没有actor属性,那么表示该角色是ultimate receiver 我们先来看一个例子: 如下1-2例: 1-2例 <?xml version = “1.0” encoding = “UTF-8” ?> <soap:Envelope> Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope” Xmlns:mi=”http://www.Monson-Haefel.com/jwsbook/message-id “ Xmlns:sec=http://schemas.xmlsoap.org/soap/security/2000-12 Xmlns:ds=http://w3c.org/2000/09/xmldsig# Xmlns:proc=”http://www.Monson-Haefel.com/jwsbook/processed-by”> <soap:Header> <mi:message-id soap:actor=”http://www.Monson-Haefel.com/logger”> 111111111111111111111111111111111:-8000 </mi:message> <proc:processed-by Soap:actor=”Http://schemas.xmlsoap.org/soap/actor/next”> <note> <time-in-millis>132132156231</time-in-millis> <identity>http://www.customer.com/</identity> <note> </proc:processed-by> <sec:Signature> <ds:signature> </ds:signature> </sec:Signature> </soap:Header> <soap:Body sec:id=”Body”> <po:purchaseOrder orderDate=”2003-09-22” …………………………………………… </po:purchaseOrder> </soap:Body> </soap:Envelope> 【重点】本例中,此消息路径的下一个接收方无论提供其他何种服务,都应该处理Processed-by文件头。 当节点处理文件头时,它必须从SOAP消息中删除该文件头。节点也可以向消息添加新的文件头。 SOAP节点通常通过修改文件头来假装的删除它(在逻辑上与删除,修改,然后再将文件头添加回SOAP消息一样)。这一点小小的技巧使节点传播文件头时能够遵循SOAP规范,不会丢失数据。 比如 1-1例中,节点可以删除Message-id文件头,但用户并不希望删除1-2例子中的processed-by文件头,因为希望消息路径中的所有节点都能向他添加信息。,因此1-1例中节点要向processed-by文件头 添加自己的数据,然后将SOAP消息传递消息路径中的下一个节点。让我们看看例子1-3吧,其实它已经删除了message-id文件头,并且修改了processed-by文件头 1-3 例子 <?xml version = “1.0” encoding = “UTF-8” ?> <soap:Envelope> Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope” Xmlns:mi=”http://www.Monson-Haefel.com/jwsbook/message-id “ Xmlns:sec=http://schemas.xmlsoap.org/soap/security/2000-12 Xmlns:ds=http://w3c.org/2000/09/xmldsig# Xmlns:proc=”http://www.Monson-Haefel.com/jwsbook/processed-by”> <soap:Header> <proc:processed-by Soap:actor=”Http://schemas.xmlsoap.org/soap/actor/next”> <note> <time-in-millis>132132156231</time-in-millis> <identity>http://www.customer.com/</identity> <note> <note> <time-in-millis>132132156231</time-in-millis> <identity>http://www.customer.com/</identity> <note> </proc:processed-by> <sec:Signature> <ds:signature> </ds:signature> </sec:Signature> </soap:Header> <soap:Body sec:id=”Body”> <po:purchaseOrder orderDate=”2003-09-22” …………………………………………… </po:purchaseOrder> </soap:Body> </soap:Envelope> mustUnderstand属性 使用next标准角色类型会有一些后遗症。在许多情况下,用户可能并不知道确切的消息路径或不知道消息路径中的各节点的功能,即用户并不总能知道各节点是否正确地处理文件头。例如:processed-by 文件头所指向的 next角色,表示它下一个节点应该处理该代码块,但如果下一个节点不能识别该文件头的话,就会出问题了。 文件头必须用mustUnderstand属性来表示处理是否为强制性处理。mustUnderstand属性由SOAP1.1命名空间http://schemas.xmlsoap.org/soap/envelop定义,该属性的值可以是“1“0”分别代表真和假。该 属性默认为“0”。 MustUnderstand属性名中的Understand表示节点必须能够根据其XML结构和命名空间识别文件头,并知道如何处理它。换句话说:【这个是我自己总结的,如果节点扮演了由文件头的actor属性表示的角 色,但没有写代码来处理文件头,那么就不会识别该文件头】 如果用户添加了中间节点但没考虑指向它的所有可能的文件头,或没有考虑next角色,那么就会产生一个SOAP异常【SOAP异常下面才讲到】,并放弃处理该消息,也就不能给下一个节点提供消息了。 1-4例子 <?xml version = “1.0” encoding = “UTF-8” ?> <soap:Envelope> Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope” Xmlns:mi=”http://www.Monson-Haefel.com/jwsbook/message-id “ Xmlns:sec=http://schemas.xmlsoap.org/soap/security/2000-12 Xmlns:ds=http://w3c.org/2000/09/xmldsig# Xmlns:proc=”http://www.Monson-Haefel.com/jwsbook/processed-by”> <soap:Header> <proc:processed-by Soap:actor=http://schemas.xmlsoap.org/soap/actor/next Soap:mustUnderstand=”1”> <note> <time-in-millis>132132156231</time-in-millis> <identity>http://www.customer.com/</identity> <note> <note> <time-in-millis>132132156231</time-in-millis> <identity>http://www.customer.com/</identity> <note> </proc:processed-by> </soap:Header> <soap:Body sec:id=”Body”> <po:purchaseOrder orderDate=”2003-09-22” …………………………………………… </po:purchaseOrder> </soap:Body> </soap:Envelope> 【补充】如果您要添加一个新的节点的话,那么该节点也要处理发送过来的SOAP消息。假设当添加的新的节点,编程人员忘记包含处理processed-by文件头的逻辑了,因此新的节点不能识别processed -by文件头,并且不知道如何处理它。又由于mustUnderstand属性设置为“1”,所以新的节点不得不抛弃SOAP消息,产生一个SOAP异常,并且返回给发送方。。。。。 如果SOAP接收方无法识别强制性文件头,那么会要求它用错误代码mustUnderstand产生一个错误(稍后介绍) 传递模式与SOAP消息 是否将错误传递回发送方取决于消息传递交换模式(MEP)是单项还是请求、响应模式。如果SOAP应用程序使用了请求、响应消息传递交互模式,则要求将SOAP错误消息传递回发送方。如果SOAP应用 程序使用的是单向消息传递模式,那么则不需要返回传递错误消息。 如果MustUnderstand属性的值为0,那么SOAP指定的处理要求则不一样。如果一个节点要完成由非强制性文件头申明的角色,而且某一应用程序没能识别该文件头(没能识别出XMK结构或命名空间), 那么它必须删除该文件头。而且,并获没有强迫它去进行处理,也没有迫使它抛弃消息。可以随意删除文件头并将消息沿消息路径传递到下一个节点。 【推荐】 接收方不应该拒绝消息,应为还没有处理(和删除)指向其它某个节点的文件头。换句话说,接收方不应该试图根据目前有哪些文件头来确定路径中位于前面的节点是否成功的处理了消息。此规则特别适 用于最终接受方,最终接受方不应该拒绝消息,因为从来不回处理用于某些未知角色的文件头。 如果接收方分局并未指向的文件头的状态开始分析并且拒绝消息,则不可能在更改消息路径是不考虑这些更改对下游造成的连锁反应,由于要求各节点“考虑其自己的业务”,因此消息路径可以变化,而且 是很动态的。添加一个新的中介体(或删除它)并不需要调整消息路径中的其他各节点。 WS-I 一致性文件头 虽然BP没有涉及到特殊类型的文件头,但它确实指定了一个表示SOAP晓得遵循BP的可选一致性文件头。如1-6例: 1-6 <?xml version=”1.0” encoding=”UTF-8”> <soap:Envelope Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope”> <soap:Header> <wsi:Claim conformsTo=http://ws-i.org/profiles/basic/1.0 Xmlns:wsi=http://ws-i.org/schemas/conformanceClaim /> </soap:Header> <soap:Body sec:id=”Body”> ……………………….. </soap:Body > </soap:Envelope> WS-I Basic Profile指出并不需要Claim文件头,它还规定“不能将消息中缺少一致性声明最为消息是否符合一个或多个配置文件的判断” SOAP消息可以为它遵循的每一个配置文件声明一个单独的Claim文件头。 Claim元素只能作为Header元素的直接子元素来声明,它不能出现在SOAP消息的其他任何部分。此外,Claim文件头总是可选的,因此它的MustUnderstand属性值不能为“1”,用户不能要求接收方处理 Claim文件头 【关于文件头的进一步说明】 SOAP文件头是扩展SOAP协议的一个功能非常强大的措施。对开发人员和开发商来说,SOAP文件头作为元数据的一中结构,使用起来要比采用其他协议中的类似机制(比如CORBA IIOP中的“服务上下 文”)更加灵活和更为容易。 Message-id和processed-by文件头知识创建的自己的定制的文件头,各社团通常会定义一般用途的SOAP文件头。这些机构主要是考虑的是强调服务质量的文件头,比如安全性,事务,消息持续性以及路 由。 BP并没有强调这些潜在标准,但WS-I最终将创建更为高级的配置文件,该文件能够将在OASIS,W3C,Microsoft,IBM,以及其他机构中使用的许多协议组合成一起。 第二章 SOAP体 【重要的概念】 虽然Header元素是可选元素,但所有SOAP消息必须包含一个Body元素。Body元素要包含应用程序专用的数据或者错误消息。应用程序专用的数据是用户希望与Web服务进行交换的信息,它可以是任意 XML数据或是提供给过程调用的任意参数。不论是哪一个数据,Body元素应包含正在交换的应用程序数据。只有错误发生时才会使用错误消息。发现了问题(如处理错误或消息构造不合适)的接收节点会 将问题传递回消息路径中位于该节点之前的发送方。SOAP消息可以携带应用程序专用的数据或错误,但不能通用时携带两者。 无论Body元素包含的是应用程序专用的数据还是错误,大多数SOAP专家认为只有SOAP消息的最终接送方才应该处理Body元素的内容。消息路径中的中介点可以浏览Body元素,但不应该以任意方式改 变内容,这一点与文件头不相同。 沿消息路径的各中介体均能处理文件头,但只有最终接收方才应该更改Body元素内容,这以点非常重要。 【SOAP消息传递模式】 第一:Document/Literal消息传递模式 在Document/Literal消息传递模式中,SOAP的Body元素包含一个XML文档段,该文档段是以个良好的XML元素,它包含独立于SOAP消息的属于XML模式和命名空间的任意应用程序数据。 第二:RPC/Literal消息传递模式 RPC/Literal消息传递模式可以使用SOAP消息对过程调用或对带参数和返回值的方法调用建立模型。在RPC/Literal消息传递模式中,总是将Body元素的内容格式化为Struts。RPC请求消息包含调用的 方法名称与输入参数。 RPC响应消息则包含了返回值和各种输出参数(或错误)。 在许多情况下,RPC/Literal消息传递模式用于以传统组建的形式提供Web服务。传统组件可以是Servlet,无状态会话Bean,javaRMI对象,CORBA对象或DCOM组件。这些组件不会显式改变XML数据, 但它们均有带参数和返回值的方法。 比如2-1例子: Package com.jwsbook.soap; Import java.rmi.RemoteException; Public interface BookQipte extends java.rmi.Remote{ //Get the whilesale price of a book Public float getBookPrice(String ISBN) throws RemoteException,InvalidISBNException; } 此例有一个名为BookQuote且使用JAX-RPC(一个J2EE Web Services端点),而且是BookQuote的远程借口。 getBookPrice()方法以ISBN(International Standard Book Number,国际标准书号)形式声明了一个参数,该参数付给每一个唯一的字符。当用户用对应的ISBN激活该方法时,Web服务返回信息。 此JAX-RPC服务端点可以使用RPC/Literal消息传递模式,Web服务使用了2个SOAP消息,即一个请求消息和一个应答消息。请求消息是从初始发送方向Web服务传递,并包含方法名getBookPrice和 ISBN字符串参数。 应答消息要传递回初始发送方,并以Float值的形式包含了信息。 下面2-2用于BookQuote Web服务的SOAP请求消息 <?xml version=”1.0” encoding=”UTF-8”> <soap:Envelope Xmlns:soap=http://schemas.xmlsoap.org/soap/enevlope Xmlns:mh=”http://www.Monson-Haefel.com/jwsbook/BookQuote”> <soap:Body> <mh:getBookPrice> <isbn>03030201</isbn> </mh:getBookPrice> </soap:Body> </soap:Envelope> 例子2-3对2-2给出了对应的响应消息 <?xml version=”1.0” encoding=”UTF-8”> <soap:Envelope Xmlns:soap=http://schemas.xmlsoap.org/soap/enevlope Xmlns:mh=”http://www.Monson-Haefel.com/jwsbook/BookQuote”> <soap:Body> <mh:getBookPrice> <result>24.99</ result> </mh:getBookPrice> </soap:Body> </soap:Envelope> RPC/Literal消息传递模式与Document/Literal消息传递不同。Document/Literal消息传递模式不会假设在消息的Body元素内所包含元素的类型与结构(但文档要遵循一些XML模式)。RPC/Literal消息传递 模式要携带一些简单的参数。RPC样式的消息传递是分布式技术中最常用的方式,其中包括EJB,CORBA,DCOM等。 所以,SOAP为RPC样式的消息传递定义了一个标准的XML格式,即RPC/Literal传递。 RPC/Literal消息传递模式指定了在SOAP消息中的Body元素中表示方法以及它们的参数的方式。 重要的是要知道RPC/Literal和Document/Literal消息传递模式与使用像JAX-RPC这样的工具的开发人员的观点可能有所区别,因为JAX-RPC可以为RPC/Literal和Document/literal提供过程调用语义。 有些人对RPC/Literal模式的有效性提出了疑问:当能够使用Document/Literal模式时,为什么还要使用RPC/Literal模式呢??毫无疑问,某些时候Document/Literal模式更为简单,而且使用 Document/Literal模式还可以采用XML模式检验。这些只有等待BP来涉及了。 消息传递模式与消息传递交换模式 我们会很容易把RPC/Literal与Document/Literal模式与单向和请求/响应消息交换模式(MEP)弄混,但两者的概念截然不同。 当我们说消息传递模式是RPC/Literal或Document/Literal模式时,通常描述的是SOAP消息的有效负载:即XML文档段或者是与远程过程调用对应的参数和返回值的XML表示。 与此相反,单向和请求/响应消息交换模式表示消息的流向,而不是消息的内容。 单向消息传递是单方向传递,而请求/响应消息传递则是双向传递。 其他的信息传递模式 我这里就不说其他的信息传递模式了,因为RPC/Literal和Document/Literal模式已经过时了,而且BP坚决反对使用这2种模式,而且它们会引起一切互操作性问题。感兴趣的朋友可以在网上搜索这2个模 式的详细信息。 第三章SOAP错误 【概念】 SOAP错误消息是SOAP应用程序用于向消息路径中的“上游”节点报告错误的机制。 SOAP错误是由接收方(指消息的中介体或最终接收方)产生的。只有使用了请求/响应消息传递模式时,才要求接收方将SOAP错误传递回发送方。 在单向消息传递模式中,接收方应该产生一个错误,并可能将其存储到某处,但不必试图将错误传递到发送方。 要将SOAP错误返回到接收方的直接发送方。比如:如果消息路径中的第三个节点产生了一个错误,那么会将该错误消息传递回消息路径中的第二个节点,并且不会传递到其他对方。也可以这样说,除非 初始发送方也是直接发送方,否则不将错误消息传递到初始发送方。 SOAP注释已经承认了SOAP错误处理的重要性,因此,BP提供了用于产生和处理SOAP错误消息的很多方案,下面我们来看具体实例吧。 【Fault元素】 Body元素中包含Fault元素的SOAP消息称为错误消息。错误消息与异常类似,即当错误发生时,就会产生错误消息。在请求/响应消息传递中要使用错误消息。当处理一个请求消息时,消息路径中的节点 会产生错误消息。当错误发生时,接收节点会将错误消息传递回上游发送方,而不是期待的应答消息。不正确的消息格式,版本的不匹配,处理文件头时的错误以及与应用程序对应用的错误均会引起错误 。 当产生错误消息时,SOAP消息的Body元素必须只能包含一个Fault元素,Fault元素本身又必须包含一个faultcode元素和一个faultstring元素,同时可以包含faultactor和detail元素(后2个可选),下面我们 看看3-1例的SOAP错误消息的: 3-1 <?xml version=”1.0” encoding=”UTF-8”> <soap:Envelope Xmlns:soap=http://schemas.xmlsoap.org/soap/enevlope Xmlns:mh=”http://www.Monson-Haefel.com/jwsbook/BookQuote”> <soap:Body> <soap:Fault> <faultcode>soap:Client</faultcode> <faultstring> The ISBN value contains invalid characters </faultstring> <faultactor>http://www.xxxxxxxxxxxxx.com</faultactor> <detail> <mh:InvalidIsbnFaultDetail> <offending-value>198333330</offending-value> <conformance-rules> …………………………. ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, </conformance-rules> </mh:InvalidIsbnFaultDetail > </detail> . </soap:Fault> </soap:Body> </soap:Envelope> 【说明】 Fault元素和它的子元素是SOAP命名空间的部分,就像SOAP的Envelope元素和Body元素的关系一样。 Fault元素可以是非限定元素,也就是说它们可以不使用SOAP前缀来限定Fault元素的子元素。 Fault元素除了可以包含faultcode,faultstring,faultactor,detail元素歪,禁止它包含其他任何直接元素。 【faultcode元素】 Faultcode元素可以使用四个标准SOAP错误代码中的任何一个来标识错误。这四个标准错误代码是:Client,Server,VersionMismatch和MustUnderstand。 需要注意的是,虽然用户可以使用任意一个错误代码,但用户只能使用上面给出的四个错误代码。。。。 错误代码的含义总是与代码(本地名称)和命名空间(前缀)相关。如(soap:Client). 如果用户使用某一个标准的SOAP错误代码,那么命名空间前缀必须映射到SOAP命名空间http://schemas.xmlsoap.org/soap/envelope/ 1 Client错误: Client错误代码表示传递SOAP消息的节点出现了错误。一般来说,如果由于消息或数据有错使接收方不能处理SOAP消息,那么会将其看成客户错误,即发送方错误。 如果消息不是格式良好的消息,或包含无效数据,或少了所期望的信息(比如专用的文 件头),接收节点会产生一个Client错误。 如3-3例: 3-3 <?xml version=”1.0” encoding=”UTF-8”> <soap:Envelope Xmlns:soap=http://schemas.xmlsoap.org/soap/enevlope Xmlns:mh=”http://www.Monson-Haefel.com/jwsbook/BookQuote”> <soap:Body> <soap:Fault> <faultcode>soap:Client</faultcode> <faultstring> The ISBN value contains invalid characters </faultstring> </detail> . </soap:Fault> </soap:Body> </soap:Envelope> 当一个节点接收到带Client码的消息时,它不应该试图重新发送同样的消息,而应该采取措施来纠正问题或异常。 2 Server错误: Server错误代码表示接收到SOAP消息的节点发生故障或不能处理SOAP消息。该错误 是由接收节点(中介体或最终接收方)引起的错误反射,而且没有通过SOAP消息本身指出任何问题。在这样的情况下,发送方可以假设SOAP消息为正确,且当暂停一段时间使接收方有时间进行恢复后 ,再重新传递它。 比如当接收节点无法连接到数据库之类的资源时,会发生一个Server错误,如3-4例: <?xml version=”1.0” encoding=”UTF-8”> <soap:Envelope Xmlns:soap=http://schemas.xmlsoap.org/soap/enevlope Xmlns:mh=”http://www.Monson-Haefel.com/jwsbook/BookQuote”> <soap:Body> <soap:Fault> <faultcode>soap:Server</faultcode> <faultstring> Database is unavailable. </faultstring> </detail> . </soap:Fault> </soap:Body> </soap:Envelope> 3 VersionMismatch错误 当接收节点不能识别SOAP消息的Envelope元素的命名空间时,就会产生一个 VersionMismatch错误。例子,如果SOAP1.1节点接收到一个SOAP1.2消息,那么就会用VersionMismatch码产生一个错误。如下例3-5: <?xml version=”1.0” encoding=”UTF-8”> <soap:Envelope Xmlns:soap=http://schemas.xmlsoap.org/soap/enevlope Xmlns:mh=”http://www.Monson-Haefel.com/jwsbook/BookQuote”> <soap:Body> <soap:Fault> <faultcode>soap:VersionMismatch</faultcode> <faultstring> Message was not SOAP 1.1 </faultstring> </detail> . </soap:Fault> </soap:Body> </soap:Envelope> VersionMismatch错误只赋给Envelope,Header,Body和Fault元素的命名空间,不适用于SOAP消息的其他部分,如文件头,XML文档版本或者Body元素中的专用元素。 4 MustUnderstand 错误 当节点接收到一个SOAP消息时,它必须分析Header元素,以确定哪些文件头指向了 该节点(如果有的话),如果文件头指向当前节点(用actor属性实现),并将MustUnderstand属性设为“1”,那么需要节点知道如何访问文件头。如果节点不能识别该文件头,那么它必须要使用 MustUnderstand码产生一个错误,如3-6例: <?xml version=”1.0” encoding=”UTF-8”> <soap:Envelope Xmlns:soap=http://schemas.xmlsoap.org/soap/enevlope Xmlns:mh=”http://www.Monson-Haefel.com/jwsbook/BookQuote”> <soap:Body> <soap:Fault> <faultcode>soap:MustUnderstand</faultcode> <faultstring> Mandatory header block not understaood </faultstring> </detail> . </soap:Fault> </soap:Body> </soap:Envelope> 5 非标准的SOAP错误代码 用户可以使用非标准的SOAP错误代码,这些错误代码是由其他机构规定的,并且属 于单独的命名空间。如3-7例: <?xml version=”1.0” encoding=”UTF-8”> <soap:Envelope Xmlns:soap=http://schemas.xmlsoap.org/soap/enevlope Xmlns:mh=http://www.Monson-Haefel.com/jwsbook/BookQuote Xmlns:wsse=”http://schemas.xmlsoap.org/ws/2002/06/secext”> <soap:Body> <soap:Fault> <faultcode>wsse:InvalidSecurityToken </faultcode> <faultstring> Mandatory header block not understaood </faultstring> </detail> . </soap:Fault> </soap:Body> </soap:Envelope> 6 faultstring元素 faultstring元素是强制性元素,它提供人能够阅读的错误描述。虽然需要faultstring元素, 但用于描述错误的文本并不是太标准化。 Faultstring元素可以使用特殊属性xml:lang表示文本消息使用的语言(可选行)。IETEF RFC1766(详细情况请参考关于IETF附录)定义了对应的有效代码集。比如可以用西班牙语文本产生一个 Client错误,如3-8: <?xml version=”1.0” encoding=”UTF-8”> <soap:Envelope Xmlns:soap=http://schemas.xmlsoap.org/soap/enevlope Xmlns:mh=”http://www.Monson-Haefel.com/jwsbook/BookQuote”> <soap:Body> <soap:Fault> <faultcode>soap:Client</faultcode> <faultstring xml:lang=”es”> El ISBN tiene Letras …………….. </faultstring> </detail> . </soap:Fault> </soap:Body> </soap:Envelope> 如果没有特别说明,没有使用xml:lang属性的话,那么假设的语言为en(英语)。 7 faultactor元素 faultactor元素表示遇到错误并产生了错误(即错误代码)的节点。如果错误节点是一个 中介体,那么需要使用faultactor元素,但如果错误节点是最终接收方,则该元素可以有可以有无。例如:假如消息路径中的一个中间节点没能识别出强制性(Mustderstand=“1”)processed-by文件头,那 么它会产生一个MustUnstandard错误。例子3-9节点必须用faultactor元素表示其本身。 <?xml version=”1.0” encoding=”UTF-8”> <soap:Envelope Xmlns:soap=http://schemas.xmlsoap.org/soap/enevlope Xmlns:mh=”http://www.Monson-Haefel.com/jwsbook/BookQuote”> <soap:Body> <soap:Fault> <faultcode>soap:MustUnderstand</faultcode> <faultstring> Mandatory header block not understaood </faultstring> <faultactor> http://www.Monson-Haefel.com/jwsbook/authenticator </faultactor> </detail> . </soap:Fault> </soap:Body> </soap:Envelope> Faultactor元素可以包含任何URI,但它通常是错误节点的Internet地址,或者是由actor属性所实用的URI(如果文件头是错误源的话)。 Faultactor既可以标识产生错误的节点,也可以标识当产生错误时所扮演的角色 8 detail元素 如果Body元素内容产生了一个错误,则必须包含错误消息的detail元素,但如果当处 理文件头时发生了错误,则不能包含该元素。例子3-10的SOAP消息详细提供了在faultstring元素中报告的无效的ISBN方面的消息。 <?xml version=”1.0” encoding=”UTF-8”> <soap:Envelope Xmlns:soap=http://schemas.xmlsoap.org/soap/enevlope Xmlns:mh=”http://www.Monson-Haefel.com/jwsbook/BookQuote”> <soap:Body> <soap:Fault> <faultcode>soap:Client</faultcode> <faultstring> The ISBN value contains invalid characters </faultstring> <faultactor>http://www.xxxxxxxxxxxxx.com</faultactor> <detail> <mh:InvalidIsbnFaultDetail> <offending-value>198333330</offending-value> <conformance-rules> …………………………. ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, </conformance-rules> </mh:InvalidIsbnFaultDetail > </detail> . </soap:Fault> </soap:Body> </soap:Envelope> 【注意】detail元素可以根据对应的XML模式包含任意数量的专用元素,这些元素可以是限定元素,也可以是非限定元素,此外,detail本身可以包含任意的限定属性,只要它们不属于SOAP1.1命名空间 http://schemas.xmlsoap.org/soap/envelope/即可。 【重要】使用空detail元素是合法的,但如果在处理原始消息的Body元素的内容时发生错误的话,则不能省略detail元素。