SOAP协议低级指南
当前位置:以往代写 > JAVA 教程 >SOAP协议低级指南
2019-06-14

SOAP协议低级指南

SOAP协议低级指南

副标题#e#

SOAP(Simple Object Access Protocal) 技能有助于实现大量异构措施僻静台之间的互操纵性,从而使存在的应用可以或许被遍及的用户所会见。SOAP是把成熟的基于HTTP的WEB技能与XML的机动性和可扩展性组合在了一起。

这篇文章带你全面回首工具长途历程挪用(ORPC)技能的过程,以辅佐你领略SOAP技能的基本,以及它降服存在技能(如CORBA和DCOM)的很多缺陷的要领。随后报告具体的SOAP编码法则,并把核心放在SOAP是奈何映射到存在的ORPC观念上的。

引言:

当我在1984年开始把计较作为我的职业的时候,大大都措施员并不体贴网络协议。可是在九十年月网络变得无所不在,此刻假如有谁利用计较机却不利用某种形式网络毗连是很不可思议的。本日,一般的措施员对成立可扩展的漫衍式应用表示出更大的乐趣,而不再只是存眷于用MFC实现本性化的可浮动半透明非矩形的Coolbars了。

措施员凡是喜欢用编程模子来思考问题,而很少思量网络协议。尽量这样做凡是是很好的,但在这篇文章中我将接头的SOAP是一个没有明明的编程模子的网络协议。这并不料味着SOAP的体系布局从基础上会改变你编程的方法。相反,SOAP的一个主要方针是使存在的应用能被更遍及的用户所利用。为了实现这个目标,没有任何SOAP API或SOAP 工具请求署理(SOAP ORB),SOAP是假设你将利用尽大概多的存在的技能。几个主要的CORBA厂商已经理睬在他们的ORB产物中支持SOAP协议。微软也理睬在未来的COM版本中支持SOAP。

DevelopMentor已经开拓了参考实现,它使得在任何平台上的任何Java或Perl措施员都可以利用SOAP。

在SOAP后头的指导理念是“它是第一个没有发现任何新技能的技能”。SOAP回收了已经遍及利用的两个协议:HTTP和XML。HTTP用于实现SOAP的RPC气势气魄的传输,而XML是它的编码模式。回收几行代码和一个XML理会器,HTTP处事器(如MS的IIS或Apache)立即成为了SOAP的ORBs。 因为今朝高出一半的Web处事器回收IIS或Apache, SOAP将会从这两个产物的遍及而靠得住的利用中获取好处。这并不料味着所有的SOAP请求必需通过Web处事器来路由,传统的Web 处事器只是分配SOAP请求的一种方法。因此Web处事如IIS或Apache对成立SOAP使能的应用是充实的,但决不是须要的。

正如这篇文章将要描写的,SOAP简朴地用XML来编码HTTP的传输内容。SOAP最常用的应用是作为一个RPC协议。为了领略SOAP奈何事情,有须要扼要回首一下RPC协议的汗青。

RPCs的汗青

成立漫衍式应用的两个主要通信模子是动静传送(常常与行列组合在一起)和请求/响应。动静通报系统答允通信任何一方在任何时间发送动静。请求/响应协议把通信模式限制在请求/响应的两边。基于动静的应用强烈地意识到它们正在与外部的并行历程举办通信,而且需要一个显式的设计气势气魄。基于请求/响应的应用更象一个单历程的应用,因为发送请求的应用或多或少被阻塞直至收到来自另一个历程的响应。这使得请求/响应通信自然地适合于RPC应用。

尽量动静通信和请求/响应各有他们的利益,他们都是可以用对方来实现的。动静系统可以用较底层的请求/响应协议来成立。如微软的Message Queue Server (MSMQ)内部回收了DCE RPC来成立大大都的节制逻辑。RPC系统也可以回收较底层的动静系统来成立。MSMQ提供的关联 ID正是为了这个目标。不管评价如何,大大都的应用仍趋向于利用RPC协议,因为它们遍及的利用,它们更简朴的设计,以及更自然的到传统的编程技能的映射。

在八十年月,两个主要的RPC协议是Sun RPC 和DCE RPC。最风行的Sun RPC应用是大大都UNIX系统所利用的Network File System (NFS)。最风行的DCE RPC应用则是Windows NT?,它回收DCE RPC 协议来实现很多系统处事。这两个协议被证明合用于很大范畴的应用。可是,在八十年月末期,面向工具技能的风靡使软件界着迷于在面向工具语言和基于RPC的通信之间成立一个纽带。

在九十年月发生的工具RPC (ORPC) 协议正是试图把面向工具和网络协议接洽起来。ORPC 和 RPC 协议的主要差异是ORPC代码化了从通信终端到语言级工具的映射。在每个ORPC请求的头中都有一个cookie,处事器端的措施能用它来定位在处事器历程中的方针工具。凡是这个cookie只是一个对数组的索引,但其它技能也常常被利用,如用标记名作为Hash表的键。

今朝两个主要的OPRC协议是DCOM 和 CORBA的 Internet Inter-ORB Protocol (IIOP) 或更一般的General Inter-ORB Protocol (GIOP)。DCOM和IIOP/GIOP的请求名目很是相似。两个协议都用一个工具端点ID来确定方针工具,用要领标识符来抉择挪用哪个要领。

#p#分页标题#e#

这两个协议主要有两点差异:主要的一点差异是回收IIOP/GIOP时,接口标识符是隐含的,因为一个给定的CORBA工具只实现一个接口(尽量OMG当前正在举办每个工具有多个接口支持的尺度化事情)。DCOM与IIOP/GIOP请求的另一个细微不同是在传输体中参数值的名目。在DCOM中,传输体用网络数据表达(NDR)的名目来写,在IIOP/GIOP中,传输体用民众数据表达(CDR)的名目来写。NDR和 CDR别离处理惩罚在各类平台上的差异的数据表达。可是在这两种名目之间有一些小的不同,这使它们彼此之间并不兼容。

在ORPC与RPC协议之间的另一个重要的差异是通信端点的定名方法。在ORPC协议中,对付ORPC端点的一些可通报的表达方法被要求在网络之间通报工具引用。在CORBA/IIOP,这个表达方法被称为可交互的工具引用(IOR)。IORs包括用紧凑名目表达的寻址信息,利用了它任何CORBA产物都可以抉择一个工具端点。在DCOM中,这种表达方法被称为OBJREF,它组合了漫衍的引用计较和端点/工具标识。CORBA和DCOM都提供了在网络上寻找工具端点的高级机制,但最终这些机制都映射回到了IORs或OBJREFs。


#p#副标题#e#

今朝的技能存在的问题?

尽量DCOM和IIOP都是牢靠的协议,业界还没有完全转向个中任何一个协议。没有融合的部门原因是文化的问题所致。并且在当一些组织试图尺度化一个或另一个协议的时候,两个协议的技能合用性就被提出质疑。传统上认为DCOM和CORBA都是公道处事器随处事器端的通信协议。可是,二者对客户随处事器端的通信都存在明明的弱点,尤其是客户机被散布在Internet上的时候。

DCOM 和 CORBA/IIOP都是依赖于单个厂商的办理方案来最大优势地利用协议。尽量两个协议都在各类平台和产物上被实现了,但现实是选定的宣布需要回收单一厂商的实现。在DCOM的环境下,这意味着每个呆板要运行在Windows NT。(尽量DCOM已经被转移到其它平台,但它只在Windows?上得到了遍及的延伸)。在CORBA环境下,这意味着每个呆板要运行同样的ORB产物。简直让两个CORBA产物用IIOP彼此挪用是有大概的,可是很多高级的处事(如安详和事务)此时凡是不是可交互的。并且,任何专门厂商为同样的呆板的通信所作的优化很难起浸染,除非所有的应用被成立在同一个ORB产物上。

DCOM 和CORBA/IIOP都依赖于周密打点的情况。两个任意的计较机使得DCOM或IIOP 在情况之外被乐成挪用(calls out of the box)的几率是很低的。出格是在思量安详性的时候尤其是这样。尽量写一个能乐成地运用DCOM或IIOP的紧缩包(shrink-wrap)应用是大概的,但这样做要比基于socket的应用要更多地存眷细节。这对付乏味但必须的设置和安装打点任务出格合用。

DCOM 和 CORBA/IIOP都依赖于相当高技能的运行情况。尽量历程内的COM好像出格简朴,但COM/DCOM长途处理惩罚措施绝对不可是几天就办理的工作。IIOP 是一个比DCOM更容易实现的协议,但两个协议都有相当多的深奥的法则来处理惩罚数据分列、范例信息和位操纵。这使得一般的措施员在没有了解ORB产物或OLE32.DLL的环境下去结构一个简朴的CORBA或DCOM挪用也变得很坚苦。

也许对DCOM和CORBA/IIOP来说,最令人难以忍受的一点是它们不能在Internet 上发挥浸染。对DCOM来说,一般用户的iMac 或便宜的运行Windows 95的PC 兼容秘密想利用你的处事器执行基于规模认证险些是不行能的。更糟的是,假如防火墙或署理处事器脱离开了客户和处事器的呆板,任何IIOP或DCOM包要通过的大概性是很低的,主要是由于大大都Internet毗连技能对HTTP协议的偏幸所致。尽量一些厂商如Microsoft, Iona和Visigenic都已经成立了通道技能,但这些产物很容易对设置错误敏感并且它们是不行交互的。

在一个处事器群落中这些问题并不能影响DCOM或IIOP的利用。因为在处事器群落中主机的数量很少(一般是成百上千,而不是成千上万),这就抵消了DCOM基于ping的生命周期打点的本钱。在处事器群落中,所有主机被一个民众打点域打点的机率很大,使得统一的设置变得大概。相对少量的呆板也能保持贸易ORB产物可节制利用的本钱,因为只需要更少量的ORB许可权。假如只有IIOP在处事器群落中被利用,就只需要少量的ORB许可权。最后,在处事器群落中所有主机有直接的IP毗连也是大概的,这就消除了与防火墙相关的DCOM和 IIOP问题。

HTTP作为一个更好的RPC

在处事器群落中利用DCOM 和CORBA 是通用的做法,但客户机则利用HTTP进入处事器群落。HTTP与RPC的协议很相似,它简朴、设置遍及,而且对防火墙比其它协议更容易发挥浸染。HTTP请求一般由Web处事器软件(如IIS和Apache)来处理惩罚,但越来越多的应用处事器产物正在支持HTTP作为除DCOM和IIOP外的又一个协议。

#p#分页标题#e#

象DCOM和IIOP一样,HTTP层通过TCP/IP举办请求/响应通信。一个HTTP的客户端用TCP毗连到HTTP处事器。在HTTP中利用的尺度端标语是80,但任何其它端口也能被利用。在成立TCP毗连后,客户端可以发送一个请求动静随处事器端。处事器在处理惩罚请求后发回一个HTTP响应动静到客户端。请求和响应动静都可以包括任意的传输体的信息,凡是用Content-Length和Content-Type的 HTTP 头来标志。下面是一个正当的HTTP请求动静:

POST /foobar HTTP/1.1

Host: 209.110.197.12

Content-Type: text/plain

Content-Length: 12

Hello, World

#p#副标题#e#

你大概已经留意到HTTP头只是一般文本。这使得用包查抄措施或基于文本的Internet东西(如telnet)来诊断HTTP问题变得更容易。HTTP基于文本的属性也使得HTTP更容易合用于在Web开拓中风行的低技能程度的编程情况。

HTTP请求的第一行包括三个组件:HTTP要领,请求-URI,协议版本。在前面的例子中,这些别离对应于POST, /foobar, 和 HTTP/1.1。Internet工程任务组(IETF)已经尺度化了数量牢靠的HTTP要领。GET是HTTP用来会见Web的要领。 POST是成立应用措施的最常用的HTTP要领。和GET纷歧样,POST答允任意数据从客户端发送随处事器端。请求URI (Uniform Resource Identifier)是一个HTTP处事器端软件,它用来识别请求的方针的简朴的标识符(它更象一个IIOP/GIOP object_key 或一个DCOM IPID)。关于URIs更多的信息请参照"URIs, URLs, and URNs"。在这个例子中协议的版本是HTTP/1.1, 它暗示遵守RFC 2616的法则。HTTP/1.1比HTTP/1.0多增加了几个特性,包罗对大块数据传输的支持以及对在几个HTTP请求之间保持TCP毗连的支持。

请求的第三行和第四行指定了请求体的尺寸和范例。Content-Length 头指定了体信息的比特数。Content-Type范例标识符指定MIME范例为体信息的语法。HTTP (象 DCE一样) 答允处事器和客户端协商用于体例信息的传输语法。大大都DCE应用回收NDR.。大大都Web应用回收text/html 或其它基于文本的语法。

留意在上面样例中Content-Length头与请求体之间的空行。差异的HTTP头被carriage-return/行码序列规定边界。这些头与体之间用别的的carriage-return/行码序列来规定边界。请求接着包罗原始字节,这些字节的语法和长度由Content-Length和Content-Type HTTP 头来识别。在这个例子中,内容是十二字节的普通文本字符串"Hello, World"。

在处理惩罚了请求之后,HTTP处事器被期望发回一个HTTP响应到客户端。响应必需包罗一个状态代码来暗示请求的功效。响应也可以包括任意的体信息。下面是一个HTTP响应动静:

200 OK

Content-Type: text/plain

Content-Length: 12

dlroW ,olleH

在这个例子中,处事器返回状态代码200,它是HTTP中尺度的乐成代码。假如处事器端不能破解请求代码,它将返回下列的响应:

400 Bad Request

Content-Length: 0

假如HTTP处事器抉择到方针URI的请求应该姑且转向别的的一个差异的URI,下列响将被返回:

307 Temporarily Moved

Location: http://209.110.197.44/foobar

Content-Length: 0

这个响应奉告客户,请求将可以或许通过从头通报它到在Location头中指定的地点来被满意。

所有的尺度状态码和头都在RFC 2616中被描写。它们中很少的内容与SOAP用户直接相关,但有一个明明的破例。在HTTP/1.1,底层的TCP毗连在多个请求/响应对之间重用。HTTP Connection头答允客户端或处事器中任何一方封锁底层的毗连。通过增加下列HTTP头到请求或响应中,两边城市要求在处理惩罚请求后封锁它们的TCP毗连:

Connection: close

当与HTTP/1.0软件交互时,为了保持TCP毗连,发起发送方插手下列HTTP头到每个请求或响应中:

Connection: Keep-Alive

这个头使缺省的HTTP/1.0协议在每次响应后从头开始TCP毗连的行为无法利用。

HTTP的一个利益是它正被遍及的利用和接管。图4暗示了一个简朴的Java措施,它发送前面暗示的请求并从响应中理会出功效字符串。

下面则是一个简朴的C措施用CGI来读取来自HTTP请求的字符串并通过HTTP响应把它的逆序串返回。

#include <stdio.h>
int main(int argc, char **argv) {
char buf[4096];
int cb = read(0, buf, sizeof(buf));
buf[cb] = 0;
strrev(buf);
printf("200 OK\r\n");p>
printf("Content-Type: text/plain\r\n");
printf("Content-Length: %d\r\n", cb);
printf("\r\n");
printf(buf);
return 0;

处事器的实现是用Java servlet,以制止CGI的每个请求一个历程的开销。

#p#分页标题#e#

一般来说CGI是耗费价钱最小的写HTTP处事器端代码的要领。实际上,每一个HTTP处事器端产物都提供了一个更有效的机制来让你的代码处理惩罚一个HTTP请求。IIS提供了ASP和ISAPI作为写HTTP代码的机制。Apache答允你用运行在Apache靠山措施中的 C或Perl来写模块。大大都应用处事器软件答允你写Java servlet,COM组件,EJB Session beans或基于可携带工具适配器(POA)接口的CORBA servants。

#p#副标题#e#

XML作为一个更好的网络数据表达方法(NDR)

HTTP是一个相当有用的RPC协议,它提供了IIOP或DCOM在组帧、毗连打点以及序列化工具应用等方面大部门成果的支持。( 并且URLs与IORs和OBJREFs在成果上令人赞叹的靠近)。HTTP所缺少的是用单一的尺度名目来表达一个RPC挪用中的参数。这则正是XML的用武之地。

象NDR和CDR,XML是一个与平台无关的中性的数据表达协议。XML答允数据被序列化成一个可以通报的形式,使得它容易地在任何平台上被解码。XML有以下差异于NDR和CDR的特点:

有大量XML编码息争码软件存在于每个编程情况僻静台上XML基于文本,相当容易用低技能程度的编程情况来处理惩罚XML是出格机动的名目,它容易用一致的方法来被扩展为支持可扩展性,在XML中每一个元素和属性有一个名域URI与它相接洽,这个URI用xmlns属性来指定。

思量下面的XML文档:

<reverse_string xmlns="urn:schemas-develop-com:StringProcs">
<string1>Hello, World</string1>
<comment xmlns=‘http://foo.com/documentation‘>
This is a comment!!
</comment>
</reverse_string>

元素<reverse_string>和<string1>的名域URI是urn:schemas-develop-com:StringProcs。元素<comment>的名域URI是http://foo.com/documentation。第二个URI也是一个URL的事实是不重要的。在这两种环境下,URI简朴地被用来消除元素<reverse_string>,<string1>,<comment>和任何可巧有同样标志名的其它元素间的歧义。

为了利便,XML答允名域URIs被映射为局部独一的前缀。这意味着下面的XML文档在语义上等同于上面的文档:

<sp:reverse_string
xmlns:sp="urn:schemas-develop-com:StringProcs"
xmlns:doc=‘http://foo.com/documentation‘
>
<sp:string1>Hello, World</sp:string1>
<doc:comment>
This is a comment!!
</doc:comment>
</sp:reverse_string>

后头的形式对作者来说更容易,尤其是假如有很多名域URIs在利用时。

XML也支持带范例的数据表达。正在推出的XML Schema类型为描写XML数据范例尺度化了一个词搜集。下面是一个元素<reverse_string>的XML Schema的描写:

<schema
xmlns=‘http://www.w3.org/1999/XMLSchema‘
targetNamespace=‘urn:schemas-develop-com:StringProcs‘
>
<element name=‘reverse_string‘>
<type>
<element name=‘string1‘ type=‘string‘ />
<any minOccurs=‘0‘ maxOccurs=‘*‘/>
</type>
</element>
</schema>

这个XML Schema界说叙述了XML名域urn:schemas-develop-com:StringProcs包括了一个名为<reverse_string>的元素,这个元素包括了一个名为string1的子元素(范例为string),它被0个或更多没有指定的元素所遵守。

XML Schema 类型还界说了一组内置的原始数据范例和成立一个XML文档中元素的范例的机制。下面的XML文档用XML Schema范例属性来把元素和范例名接洽在一起:

<customer
xmlns=‘http://customer.is.king.com‘
xmlns:xsd=‘http://www.w3.org/1999/XMLSchema‘
>
<name xsd:type=‘string‘>Don Box</name>
<age xsd:type=‘float‘>23.5</name>
</customer>

#p#副标题#e#

毗连XML文档事例到XML Schema描写的新的一个机制在本文写作的时候正在尺度化进程中。

HTTP + XML = SOAP

SOAP把XML的利用代码化为请求和响应参数编码模式,并用HTTP作传输。这好像有点抽象。详细地讲,一个SOAP要领可以简朴地看作遵循SOAP编码法则的HTTP请求和响应。一个SOAP终端则可以看作一个基于HTTP的URL,它用来识别要领挪用的方针。象CORBA/IIOP一样,SOAP不需要详细的工具被绑定到一个给定的终端,而是由详细实现措施来抉择奈何把工具终端标识符映射随处事器端的工具。

#p#分页标题#e#

SOAP请求是一个HTTP POST请求。SOAP请求的content-type必需用text/xml。并且它必需包括一个请求-URI。处事器奈何表明这个请求-URI是与实现相关的,可是很多实现中大概用它来映射到一个类可能一个工具。一个SOAP请求也必需用SOAPMethodName HTTP头来指明将被挪用的要领。简朴地讲,SOAPMethodName头是被URI指定范畴的应用相关的要领名,它是用#符作为脱离符将要领名与URI支解开:

SOAPMethodName: urn:strings-com:IString#reverse

这个头表白要领名是reverse,范畴URI是urn:strings-com:Istring。 在SOAP中,划定要领名范畴的名域URI在成果上等同于在DCOM 或 IIOP中划定要领名范畴的接口ID。

简朴的说,一个SOAP请求的HTTP体是一个XML文档,它包括要领中[in]和[in,out]参数的值。这些值被编码成为一个显著的挪用元素的子元素,这个挪用元素具有SOAPMethodName HTTP头的要领名和名域URI。挪用元素必需呈此刻尺度的SOAP <Envelope>和<Body>元素内(后头会更多接头这两个元素)。下面是一个最简朴的SOAP要领请求:

POST /string_server/Object17 HTTP/1.1
Host: 209.110.197.2
Content-Type: text/xml
Content-Length: 152
SOAPMethodName: urn:strings-com:IString#reverse
<Envelope>
<Body>
<m:reverse xmlns:m=‘urn:strings-com:IString‘>
<theString>Hello, World</theString>
</m:reverse>
</Body>
</Envelope>

SOAPMethodName头必需与<Body>下的第一个子元素相匹配,不然挪用将被拒绝。这答允防火墙打点员在不理会XML的环境下有效地过滤对一个详细要领的挪用。

SOAP响应的名目雷同于请求名目。响应体包括要领的[out]和 [in,out]参数,这个要领被编码为一个显著的响应元素的子元素。这个元素的名字与请求的挪用元素的名字沟通,但以Response后缀来毗连。下面是对前面的SOAP请求的SOAP响应:

200 OK
Content-Type: text/xml
Content-Length: 162
<Envelope>
<Body>
<m:reverseResponse xmlns:m=‘urn:strings-com:IString‘>
<result>dlroW ,olleH</result>
</m:reverseResponse>
</Body>
</Envelope>

这里响应元素被定名为reverseResponse,它是要领名紧跟Response后缀。要留意的是这里是没有SOAPMethodName HTTP头的。这个头只在请求动静中需要,在响应动静中并不需要。

让很多SOAP新手狐疑的是SOAP中没有关于SOAP处事器奈何利用请求头来分发请求的要求;这被留为一个实现上的细节。一些SOAP处事器将映射请求-URIs到类名,并分配挪用到静态要领或到在请求一连期内存活的类的实例。其它SOAP处事器则将请求-URIs映射到始终存活的工具,常常是用查询字符串来编码一个用来定位在处事器历程中的工具要害字。尚有一些其它的SOAP处事器用HTTP cookies来编码一个工具要害字,这个要害字可被用来在每次要领请求中规复工具的状态。重要的是客户对这些区别并不知道。客户软件只是简朴遵循HTTP和XML的法则来形成SOAP请求,让处事器自由以它认为最符合的方法来为请求处事。

#p#副标题#e#

SOAP体的焦点

SOAP的XML特性是为把数据范例的实例序列化成XML的编码模式。为了到达这个目标,SOAP不要求利用传统的RPC气势气魄的署理。而是一个SOAP要领挪用包括至少两个数据范例:请求和响应。思量这下面个COM IDL代码:

[ uuid(DEADF00D-BEAD-BEAD-BEAD-BAABAABAABAA) ]
interface IBank : IUnknown {
HRESULT withdraw([in] long account,
[out] float *newBalance,
[in, out] float *amount
[out, retval] VARIANT_BOOL *overdrawn);
}

在任何RPC协议下,account和amount参数的值将呈此刻请求动静中,newBalance,overdrawn参数的值,尚有amount参数的更新值将呈此刻响应动静中。

SOAP把要领请求和要领响应晋升到了一流状态。在SOAP中,请求和响应实际上范例的实例。为了领略一个要领好比IBank::withdraw奈何映射一个SOAP请求和响应范例,思量下列的数据范例:

struct withdraw {
long account;
float amount;
};

这是一个所有的请求参数被打包成为一个单一的数据范例。同样下面的数据暗示打包所有响应参数到一个单一的数据范例。

struct withdrawResponse {
float newBalance;
float amount;
VARIANT_BOOL overdrawn;
};

再给出下面的简朴的Visual Basic措施,它利用了以前界说的Ibank接口:

Dim bank as IBank
Dim amount as Single
Dim newBal as Single
Dim overdrawn as Boolean
amount = 100
Set bank = GetObject("soap:http://bofsoap.com/am")
overdrawn = bank.withdraw(3512, amount, newBal)

#p#分页标题#e#

你可以或许想象底层的署理(大概是一个SOAP,DCOM,或IIOP署理)看上去象图8中所暗示的那样。这里,在发送请求动静之前,参数被序列化成为一个请求工具。同样被响应动静吸收到的响应工具被反序列化为参数。一个雷同的转变同样产生在挪用的处事器端。

当通过SOAP挪用要领时,请求工具和响应工具被序列化成一种已知的名目。每个SOAP体是一个XML文档,它具有一个显著的称为<Envelope>的根元素。标志名<Envelope>由SOAP URI (urn:schemas-xmlsoap-org:soap.v1)来规定范畴,所有SOAP专用的元素和属性都是由这个URI来规定范畴的。SOAP Envelope包括一个可选的<Header>元素,紧跟一个必需的<Body>元素。<Body>元素也有一个显著的根元素,它可能是一个请求工具可能是一个响应工具。下面是一个IBank::withdraw请求的编码:

<soap:Envelope
xmlns:soap=‘urn:schemas-xmlsoap-org:soap.v1‘>
<soap:Body>
<IBank:withdraw xmlns:IBank=
‘urn:uuid:DEADF00D-BEAD-BEAD-BEAD-BAABAABAABAA‘>
<account>3512</account>
<amount>100</amount>
</IBank:withdraw>
</soap:Body>
</soap:Envelope>

下列响应动静被编码为:

<soap:Envelope
xmlns:soap=‘urn:schemas-xmlsoap-org:soap.v1‘>
<soap:Body>
<IBank:withdrawResponse xmlns:IBank=
‘urn:uuid:DEADF00D-BEAD-BEAD-BEAD-BAABAABAABAA‘>
<newBalance>0</newBalance>
<amount>5</amount>
<overdrawn>true</overdrawn>
</IBank:withdrawResponse>
</soap:Body>
</soap:Envelope>

留意[in, out]参数呈此刻两个动静中。

在查抄了请求和响应工具的名目后,你大概已经留意到序列化名目凡是是:

<t:typename xmlns:t=‘namespaceuri‘> ;
<fieldname1>field1value</fieldname1>
<fieldname2>field2value</fieldname2>
</t:typename>

#p#副标题#e#

在请求的环境下,范例是隐式的C气势气魄的布局,它由对应要领中的[in]和[in, out]参数构成。对响应来说,范例也是隐式的C气势气魄的布局,它由对应要领中的[out]和[in, out]参数构成。这种每个域对应一个子元素的气势气魄有时被称为元素正规名目(ENF)。一般环境下,SOAP只用XML特性来转达描写包括在元素内容中信息的注释。

象DCOM和IIOP一样,SOAP支持协议头扩展。SOAP用可选的<Header>元素来传载被协议扩展所利用的信息。假如客户端的SOAP软件包括要发送头信息,原始的请求将大概如图9所示。在这种环境下定名causality的头将与请求一起序列化。收到请求后,处事器端软件能查察头的名域URI,并处理惩罚它识别出的头扩展。这个头扩展被http://comstuff.com URI识别,并等候一个如下的工具:

struct causality {
UUID id;
};

在这种环境下的请求,假如头元素的URI不能被识别,头元素可以被安详地忽略。

但你不能安详的忽略所有的SOAP体中的头元素。假如一个特定的SOAP头对正确处理惩罚动静是很要害的,这个头元素能被用SOAP属性mustUnderstand=’true’标志为必需的。这个属性汇报吸收者头元素必需被识别并被处理惩罚以确保正确的利用。为了强迫前面causality头成为一个必需的头,动静将被写成如下形式:

<soap:Envelope
xmlns:soap=‘urn:schemas-xmlsoap-org:soap.v1‘>
<soap:Header>
<causality
soap:mustUnderstand=‘true‘
xmlns="http://comstuff.com">
<id>362099cc-aa46-bae2-5110-99aac9823bff</id>
</causality>
</soap:Header>
<!— soap:Body element elided for clarity —>
</soap:Envelope>

SOAP软件碰着不能识别必需的头元素环境时,必需拒绝这个动静并出示一个错误。假如处事器在一个SOAP请求中发明一个不能识此外必需的头元素,它必需返回一个错误响应而且不发送任何挪用到方针工具。假如客户端在一个SOAP请求中发明一个不能识别出的必需的头元素,它必需向挪用者返回一个运行时错误。(在COM环境下,这将映射为一个明明的HRESULT)

SOAP数据范例

在SOAP动静中,每个元素大概是一个SOAP布局元素,一个根元素,一个存取元素或一个独立的元素。在SOAP中,soap:Envelope, soap:Body和 soap:Header 是独一的三个布局元素。它们的根基干系由下列XML Schema所描写:

#p#分页标题#e#

<schema
targetNamespace=‘urn:schemas-xmlsoap-org:soap.v1‘>
<element name=‘Envelope‘>
<type>
<element name=‘Header‘ type=‘Header‘
minOccurs=‘0‘ />
<element name=‘Body‘ type=‘Body‘
minOccurs=‘1‘ />
</type>
</element>
</schema>

在SOAP元素的四种范例中,除了布局元素外都被用作表达范例的实例或对一个范例实例的引用。

根元素是显著的元素,它是soap:Body 或是 soap:Header的直接的子元素。个中soap: Body只有一个根元素,它表达挪用、响应或错误工具。这个根元素必需是soap:Body的第一个子元素,它的标志名和域名URI必需与HTTP SOAPMethodName头或在错误动静环境下的soap:Fault相对应。而soap:Header元素有多个根元素,与动静相接洽的每个头扩展对应一个。这些根元素必需是soap:Header的直接子元素,它们的标志名和名域URI暗示当前存在扩展数据的范例。

存取元素被用作表达范例的域、属性或数据成员。一个给定范例的域在它的SOAP表达将只有一个存取元素。存取元素的标志名对应于范例的域名。思量下列Java 类界说:

package com.bofsoap.IBank;
public class adjustment {
public int account ;
public float amount ;
}

在一个SOAP动静中被序列化的实譬喻下所示:

<t:adjustment
xmlns:t=‘urn:develop-com:java:com.bofsoap.IBank‘>
<account>3514</account>
<amount>100.0</amount>
</t:adjustment>

#p#副标题#e#

在这个例子中,存取元素account和amount被称着简朴存取元素,因为它们会见对应于在W3C XML Schema类型 (见 http://www.w3.org/TR/XMLSchema-2) 的Part 2中界说的原始数据范例的值。这个类型指定了字符串,数值,日期等数据范例的名字和表达方法以及利用一个新的模式界说中的<datatype>布局来界说新的原始范例的机制。

对引用简朴范例的存取元素,元素值被简朴地编码为直接在存取元素下的字符数据,如上所示。对引用组合范例的存取元素(就是那些自身用子存取元素来结构的存取元素),有两个技能来对存取元素举办编码。最简朴的要领是把被布局化的值直接嵌入在存取元素下。思量下面的Java类界说:

package com.bofsoap.IBank;
public class transfer {
public adjustment from;
public adjustment to;
}

假如用嵌入值编码存取元素,在SOAP中一个序列化的transfer工具如下所示:

<t:transfer
xmlns:t=‘urn:develop-com:java:com.bofsoap.IBank‘
>
<from>
<account>3514</account>
<amount>-100.0</amount>
</from>
<to>
<account>3518</account>
<amount>100.0</amount>
</to>
</t:transfer>

在这种环境下,adjustment工具的值被直接编码在它们的存取元素下。

在思量组合存取元素时,需要说明几个问题。先思量上面的transfer类。类的from和to的域是工具引用,它大概为空。SOAP用XML Schemas的null属性来暗示空值或引用。下面例子暗示一个序列化的transfer工具,它的from域是空的:

<t:transfer
xmlns:t=‘urn:develop-com:java:com.bofsoap.IBank‘
xmlns:xsd=‘http://www.w3.org/1999/XMLSchema/instance‘
>
<from xsd:null=‘true‘ />
<to>
<account>3518</account>
<amount>100.0</amount>
</to>
</t:transfer>

在不存在的环境下, xsd:null属性的隐含值是false。给定元素的可否为空的属性是由XML Schema界说来节制的。譬喻下列XML Schema将只答允from存取元素为空:

<type name=‘transfer‘ >
<element
name=‘from‘
type=‘adjustment‘
nullable=‘true‘
/>
<element
name=‘to‘
type=‘adjustment‘
nullable=‘false‘ <!— false is the default —>
/>
</type>

#p#副标题#e#

在一个元素的Schema声明中假如没有nullable属性,就意味着在一个XML文档中的元素是不能为空的。Null存取元素的准确名目当前还在修订中&#0;要相识用更多信息参考最新版本的SOAP类型。

与存取元素相关的另一个问题是由于范例干系引起的可代换性。由于前面的adjustment类不是一个final范例的类,transfer工具的from和to域实际引用担任范例的实例是大概的。为了支持这种范例兼容的替换,SOAP利用一个名域限定的范例属性的XML Schema约定。这种范例属性的值是一个对元素详细的范例的限制的名字。思量下面的adjustment扩展类:

package com.bofsoap.IBank;
public class auditedadjustment extends adjustment {
public int auditlevel;
}

给出下面Java语言:

#p#分页标题#e#

transfer xfer = new transfer();
xfer.from = new auditedadjustment();
xfer.from.account = 3514; xfer.from.amount = -100;
xfer.from.auditlevel = 3;
xfer.to = new adjustment();
xfer.to.account = 3518; xfer.from.amount = 100;

在SOAP中transfer工具的序列化形式如下所示:

<t:transfer
xmlns:xsd=‘http://www.w3.org/1999/XMLSchema‘
xmlns:t=‘urn:develop-com:java:com.bofsoap.IBank‘
>
<from xsd:type=‘t:auditedadjustment‘ >
<account>3514</account>
<amount>-100.0</amount>
<auditlevel>3</auditlevel >
</from>
<to>
<account>3518</account>
<amount>100.0</amount>
</to>
</t:transfer>

在这里xsd:type属性引用一个名域限定的范例名,它能被反序列化措施用于实例化工具的正确范例。因为to存取元素引用到一个被预料的范例的实例(而不是一个可取代的担任范例),xsd:type属性是不需要的。

适才的transfer类设法回避了一个要害问题。假如正被序列化的transfer工具用下面这种方法初始化将会产生什么环境:

transfer xfer = new transfer();
xfer.from = new adjustment();
xfer.from.account = 3514; xfer.from.amount = -100;
xfer.to = xfer.from;

基于以前的议论,在SOAP 中transfer工具的序列化形式如下所示:

<t:transfer
xmlns:t=‘urn:develop-com:java:com.bofsoap.IBank‘>
<from>
<account>3514</account>
<amount>-100.0</amount>
</from>
<to>
<account>3514</account>
<amount>-100.0</amount>
</to>
</t:transfer>

这个表达有两个问题。首先最容易领略的问题是同样的信息被发送了两次,这导致了一个比实际所需要动静的更大的动静。一个更微妙的可是更重要的问题是由于反序列化措施不能判别两个带有同样值的adjustment工具与在两个处所被引用的一个单一的adjustment工具的区别,两个存取元素间的身份干系就被丢失。假如这个动静吸收者已经在功效工具上执行了下面的测试,(xfer.to == xfer.from)将不会返回true。

void processTransfer(transfer xfer) {
if (xfer.to == xfer.from)
handleDoubleAdjustment(xfer.to);
else
handleAdjustments(xfer.to, xfer.from);
}

(xfer.to.equals(xfer.from))大概返回true的事实只是较量了两个存取元素的值而不是它们身份。

#p#副标题#e#

为了支持必需保持身份干系的范例的序列化,SOAP支持多引用存取元素。今朝我们打仗到的存取元素是单引用存取元素,也就是说,元素值是嵌入在存取元素下面的,并且其它存取元素被答允引用谁人值(这很雷同于在NDR中的[unique]的观念)。多引用存取元素老是被编码为只包括已知的soap:href属性的空元素。soap:href属性老是包括一个代码片断标识符,它对应于存取元素引用到的实例。假如to和from存取元素已经被编码为多引用存取元素,序列化的transfer工具如下所示:

<t:transfer
xmlns:t=‘urn:develop-com:java:com.bofsoap.IBank‘>
<from soap:href=‘#id1‘ />
<to soap:href=‘#id1‘ />
</t:transfer>

这个编码假设与adjustment类兼容的一个范例的实例已经在envelope中的其它处所被序列化,并且这个实例已经被用soap:id属性标志,如下所示:

<t:adjustment soap:id=‘id1‘
xmlns:t=‘urn:develop-com:java:com.bofsoap.IBank‘>
<account>3514</account>
<amount>-100.0</amount>
</t:adjustment>

对多引用存取元素,把代码段的标识符(譬喻#id1)解析到正确的实例是反序列化措施的事情。

前面的接头表明白多引用存取元素奈何与它的方针实例相关联。下面要接头的是方针实例在那边被序列化。这就干系到独立元素和包的观念。

独立元素

在SOAP中,一个独立元素暗示至少被一个多引用存取元素引用的范例的实例。所有的独立元素用soap:id属性作标志,并且这个属性的值在整个SOAP envelope中必需是独一的。独立的元素被编码就好象是它们被一个存取元素打包,这个存取元素的标志名是实例的名域限制的范例名。在上面的例子中,实例的名域限制的范例名是t:adjustment。

#p#分页标题#e#

SOAP限制独立元素能被编码的场合。SOAP界说了一个能合用于任何元素的属性:(soap:Package)。这个属性被用于节制独立元素能在那边被解码。SOAP序列化法则指出独立元素必需编码为soap:Header元素或soap:Body元素的直接子元素,可能是任何其它标志为soap:Package=‘true’的元素。通过把一个元素注释为包,你能担保编码谁人实例的XML元素是完全自包括的,而且在这个包以外没有任何引用到这个元素的多引用存取元素。

假设transfer 类对应于一个要领请求。假如transfer范例不是一个包,被to和from存取元素引用的独立元素将作为soap:Body元素的直接子元素呈现,如图10所示。假如transfer范例是一个正当的SOAP包范例,编码大概象图11所示。留意,因为transfer元素是一个包,所有多引用存取器元素都引用被包括的元素。这使得把transfer元素当作一个能从它的父辈元素中疏散出的独立的XML代码段变得更为容易。

多引用存取元素老是引用独立元素的模子是有一个破例的。SOAP答允包括字符串和二进值数据的存取元素是多引用存取元素的方针。这意味着下面的代码是正当的:

<t:mytype>
<field1 soap:href="http://www.bianceng.cn/index.php#id1" />
<field2 soap:id="id1">Hello, SOAP</field2>
</t:mytype>

尽量事实是存取元素2有一个soap:id属性,它实际上是一个存取元素而不是独立元素。

#p#副标题#e#

SOAP数组

数组被编码为组合范例的一个非凡的例子。在SOAP中,一个数组必需有一个秩(维数)和一个容量。一个数组被编码为一个组合范例,个中每一个数组元素被编码为一个子元素,这个子元素的名字是元素的名域限制的范例名。

假设有下面的COM IDL范例界说:

struct POINTLIST {
long cElems;
[size_is(cElems)] POINT points[];
};

这个范例的实例将被序列化为:

<t:POINTLIST xmlns:t=‘uri for POINTLIST‘>
<cElems>3</cElems>
<points xsd:type=‘t:POINT[3]‘ >
<POINT>lt;x>3</x>lt;y>4</y>lt;/POINT>
<POINT>lt;x>7</x>lt;y>5</y>lt;/POINT>
<POINT>lt;x>1</x>lt;y>9</y>lt;/POINT>
</points>
<t:POINTLIST>

假如points域被标志为[ptr]属性,这个编码将用一个多引用存取元素,如下所示:

<t:POINTLIST xmlns:t=‘uri for POINTLIST‘>
<cElems>3</cElems>
<points soap:href="http://www.bianceng.cn/index.php#x9" />
</t:POINTLIST>
<t:ArrayOfPOINT soap:id=‘x9‘ xsd:type=‘t:POINT[3]‘>
<POINT>lt;x>3</x>lt;y>4</y>lt;/POINT>
<POINT>lt;x>7</x>lt;y>5</y>lt;/POINT>
<POINT>lt;x>1</x>lt;y>9</y>lt;/POINT>
</t:ArrayOfPOINT>

当把一个数组编码为一个独立元素时,标志名是带前缀ArrayOf的范例名。

象NDR和CDR一样,SOAP支持部门转换的数组。假如子元素的数量少于所声明的容量,这些元素被假设正从数组的末端丢失。这可以或许通过在正包括的数组元素上利用soap:offset属性来被忽略。

<t:ArrayOfPOINT soap:id=‘x9‘ xsd:type=‘t:POINT[5]‘
soap:offset=‘[1]‘>
<POINT>lt;x>1</x>lt;y>9</y>lt;/POINT>
</t:ArrayOfPOINT>

soap:offset属性暗示呈此刻数组中的第一个元素的索引。在上面的例子中,元素0,2到4都是不被转换的。SOAP也支持稀疏数组,这是通过利用soap:position属性来把每个元素用它的绝对索引来注释而实现的:

<t:ArrayOfPOINT soap:id=‘x9‘ xsd:type=‘t:POINT[9]‘>
<POINT soap:position=‘[3]‘>lt;x>3</x>lt;y>4</y>lt;/POINT>
<POINT soap:position=‘[7]‘>lt;x>4</x>lt;y>5</y>lt;/POINT>
</t:ArrayOfPOINT>

在这个例子中,元素0到2,4到6,以及8到9都不是被转换的。

请留意,在SOAP中数组的准确语法在这篇文章写作时还在被从头审查以调解到即将推出的W3C XML Schema类型中。要不绝相识SOAP类型的最新版原来得到更多的细节。

#p#副标题#e#

错误处理惩罚

#p#分页标题#e#

一个处事器有时将不能正确地为一个要领请求提供处事。这大概是由于一般的HTTP错误造成的(如请求-URI不能被映射到当地的资源或一个HTTP级的安详违反)。也大概是在SOAP翻译软件中的问题,如马歇尔打包错误或一个必需的头不能被认出。其它大概的原因包罗一个请求不能正确地被处事,可能应用/工具代码抉择要返回一个应用级的错误给挪用者。这些环境在SOAP类型中都被清楚地加以处理惩罚。

假如在分发对任何SOAP代码的挪用之前一个错误产生在HTTP层,一个纯HTTP响应必需被返回。尺度的HTTP状态代码编号将被回收,400级的代码暗示一个客户激发的错误,500级的代码暗示处事器激发的错误。这凡是在代码执行前由Web处事器软件自动处理惩罚。

假设在HTTP层一切正常,错误产生的下一个处所是在那些翻译和分发对应用代码(如COM工具和CORBA伺服工具)的SOAP挪用。假如错误产生在这一层,处事器必需返回一个错误动静来取代一个尺度的响应动静。一个错误动静是下列被编码为soap:Body的根元素的范例的实例。

<schema targetNamespace=‘urn:schemas-xmlsoap-org:soap.v1‘ >
<element name=‘Fault‘>
<type>
<element name=‘faultcode‘ type=‘string‘ />
<element name=‘faultstring‘ type=‘string‘ />
<element name=‘runcode‘ type=‘string‘ />
<element name=‘detail‘ />
</type>
</element>

</schema>

faultcode存取元素必需包括一个用已知的整数暗示的SOAP错误代码可能一个专门应用的名域限制的值。当前的SOAP 错误代码如图12所示。Faultstring存取元素包括对产生的错误的可读性的描写。runcode 存取元素包括一个字符串,它的值必需是Yes, No或 Maybe,表白被请求的操纵实际上是否在错误发生之前被执行。Detail存取元素是可选的,用于包括一个专门应用的异常工具。

下面是一个对应于一个包括无法识此外必需的头元素的请求的SOAP错误的例子:

<soap:Envelope
xmlns:soap=‘urn:schemas-xmlsoap-org:soap.v1‘
>
<soap:Body>
<soap:Fault> ;
<faultcode>200</faultcode>
<faultstring>
Unrecognized ‘causality‘ header
</faultstring>
<runcode>No</runcode>
</soap:Fault>
</soap:Body>
</soap:Envelope>

假设详细应用的错误需要被返回,你大概看到如图13所示的代码。在应用界说的错误的环境下,思量应用的异常/错误工具时detail存取元素起到了soap:Body 元素的浸染。

机密

一个遗留的HTTP问题还需要进一步阐发。SOAP支持(但不需要)HTTP扩展框架约定来指定必需的HTTP头扩展。这些约定主要有两个目标。首先,它们答允任意的URI被用于限定给定的HTTP头的范畴(象XML名域一样)。第二,这些约定答允把必需的头与可选的头区分隔来(象soap:mustUnderstand)。下面是一个利用HTTP扩展框架来把SOAPMethodName头界说成为一个必需的头扩展:

M-POST /foobar HTTP/1.1
Host: 209.110.197.2
Man: "urn:schemas-xmlsoap-org:soap.v1; ns=42"
42-SOAPMethodName: urn:bobnsid:IFoo#DoIt

Man头映射SOAP URI到前缀为42的头,并暗示没有认出SOAP的处事器必需返回一个HTTP错误,状态代码为501 (没有被实现) 或 510 (没有被扩展)。HTTP要领必需是M-POST,表白今朝是必需的头扩展。

结论

SOAP是一个被范例化的序列化名目,它刚巧用HTTP 作为请求/响应动静传输协议。SOAP被设计为与正将呈现的XML Schema类型密切共同,并支持在Internet的任那里所运行的COM, CORBA, Perl, Tcl, 和 Java-language, C, Python, 或 PHP 等措施间的互操纵性。

但愿本文给了你一个对这个协议详细细节的更清晰的领略。我勉励你用SOAP举办尝试,可能试着利用SOAP使能的系统之一(列在http://www.develop.com/soap/),可能本身做一些事情。我本人发明回收剧本语言(Jscript),使一个根基的SOAP客户与处事器成立并运行只耗费了不到一个小时。针对你对HTTP和XML的熟悉水平,以及你的方针平台的成熟度,你所耗费的时间会有所差异。

    关键字:

在线提交作业