级别: 初级 李 劲 (jinli@ca.ibm.com)IBM软件解决方案多伦多实验室应用开发技术中心
2002 年 10 月 01 日 动态电子商务是电子商务发展的目标,而Web服务是其核心技术,也是Web的下一个革新。Web服务将改变企业之间的商务运作和企业对企业(B2B)的应用的设计与开发。本文摘选自即将出版的
《动态电子商务的web服务》一书第五章,也是调用Web服务系列的第一部分。讲述了SOAP的架构、基本技术知识、在Web服务应用上常用的技术。
SOAP:简单的对象访问协议
中间件(Application Middleware)和网络出版(Web Publishing),两个以前基本上无关的专业被SOAP连接起来,开始合而为一。所以,来自这两个不同专业背景的人对SOAP的了解可能有点不同。在说明SOAP的架构和基本技术知识之前,先介绍SOAP的来源和历史,这样有利于讲述SOAP时,容易使广大的读者达成共识。
SOAP以RPC为一致性的调用途径,使用HTTP传送XML消息。RPC提高分布式应用程序开发者的效率,因为开发者无需再费时编程网络底层协议的代码。CORBA(Common Object Request Broker Architecture),DCOM(Distributed Component Object Model)和RMI(Remote Method Invocation)都是实现RPC的面向对象的机制。
代码表5-1 RMI示例
package rmitest;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface hello extends Remote
{
public void sayHello(String name) throws RemoteException;
} |
从代码表5-1的示例中,可以看到这个源代码与其他的Java接口定义没有很大的区别,它需要扩充java.rmi.Remote接口,并且每个方法要投java.rmi.RemoteException。中间件管理有关的网络底层的传送和协议,但是各个中间件使用的专用协议通常不能穿过防火墙在因特网上进行通信交互。另外,中间件只是为程序员开发提供了方便,可是部署开发了的应用却不容易。以前网站主要是使用HTTP协议方便用户浏览网页,但是现在也需要为软件程序(比如搜索代理Search Agent)提供信息。所以,最理想是把中间件、XML和Web合并在一起,而SOAP(Simple Object Access Protocol)就是这种技术标准规范。
那什么是SOAP呢?简单对象访问协议是一个基于XML在分布式的环境中交换信息的简单的协议。SOAP的1.1版本由DevelopMentor、IBM、Microsoft和Userland一起开发,并且在2000年5月完成草案。它包括IBM和Lotus的技术,具有供应商中立性、跨平台、编程语言和对象模型中立性等优点。最新草案1.2版本在2002年6月完成,由W3C成立的XML Protocol Working Group专注开发SOAP规范。SOAP的正式W3C标准规范可在
http://www.w3.org/TR/soap12找到。图5-1所示为SOAP的架构,它定义一个消息框架、编码规则和协议绑定。
图5-1 SOAP的架构
SOAP的架构的底层是传送协议如HTTP和SMTP,通过绑定与消息框架连接。SOAP的两个主要设计目标是简单性和可扩展性。这就意味着一些传统消息系统或分布式对象系统中的某些性质不属于SOAP规范,例如分布式垃圾收集(Distributed Garbage Collection)和对象引用(Object By Reference)。SOAP包括下面的4个部分:
- 信封(Envelope):定义了一个消息框架,描述消息的内容是什么,是谁发送的,谁应当接受并处理它以及如何处理。
- 编码规则(Encoding Rules):用于表示应用程序需要使用的数据类型的实例。
- 绑定(Binding):定义底层通信协议,进行消息交换。
- RPC:表示远程过程调用和应答的协定。
如果简化对SOAP的理解,它可以说是一个XML+RPC over HTTP的开放标准协议,方便Web服务提供者和服务请求者穿过防火墙在因特网上进行通信交互。SOAP是个跨平台的协议,每一个通过网络的远程调用都可以通过SOAP封装起来。SOAP使用HTTP传送XML消息,尽管HTTP不是最有效率的通信协议,而且在传送XML消息时还需要额外的文件解析,但是XML和HTTP都是开放标准规范,HTTP是一个在Web上被最广泛应用又能避免许多关于防火墙问题的传送协议,从而使SOAP得到了广泛的接受和应用。
SOAP请求/响应消息示例
下面用一个示例来演示SOAP消息的请求和响应,从而深入了解它。代码表5-2所示为电话号码簿员工查询Web服务示例的一个SOAP请求消息。
代码表5-2 SOAP请求消息示例
POST /TelephoneDirectoryWeb/servlet/rpcrouter HTTP/1.0
Host: localhost:8080
Content-Type: text/xml; charset=utf-8
Content-Length: nnnn
SOAPAction: ""
<?xml version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/
soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SOAP-ENV:Body>
<ns1:findEmployeeByID
xmlns:ns1="http://tempuri.org/com.acme.TelephoneDirectory"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/
soap/encoding/">
<id xsi:type="xsd:int">123</id>
</ns1:findEmployeeByID>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
|
代码表5-3所示为相应的SOAP响应消息。
代码表5-3 SOAP响应消息示例
HTTP/1.1 200 OK
Server: WebSphere Application Server/4.0
Content-Type: text/xml; charset=utf-8
Content-Length: nnnn
Connection: close
<?xml version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/
soap/envelope/"
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SOAP-ENV:Body>
<ns1:findEmployeeByIDResponse
xmlns:ns1="http://tempuri.org/com.acme.TelephoneDirectory"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/
soap/encoding/">
<return xmlns:ns2="http://www.telephonedirectory.com/
schemas/TelephoneDirectoryRemoteInterface"
xsi:type="ns2:com.acme.Employee">
<phone xsi:type="xsd:string">123-456-7890</phone>
<lastName xsi:type="xsd:string">Doe</lastName>
<firstName xsi:type="xsd:string">John</firstName>
<id xsi:type="xsd:int">123</id>
</return>
</ns1:findEmployeeByIDResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope> |
这两个示例均使用HTTP绑定传送SOAP消息。请求消息包含了调用的Web服务操作名称(findEmployeeByID),其中包含了一个简单的员工号码的参数值(123),但是XML元素findEmployeeByID不是由SOAP自身定义的。该请求的服务响应消息包含了一个获取员工信息的信息块,由WSDL文档的message元素定义的findEmployeeByIDResponse。下面将详细解释这个SOAP消息请求和响应的示例。首先,每个消息都由一个SOAP信封包装而成。SOAP定义以下4个XML元素:
- 信封(env:Envelope)
由于上面的示例使用的是SOAP 1.1标准规范,所以这个XML元素用SOAP-ENV:Envelope来代表。这是1.1和1.2版本的区别之一,但是只要表示的URI一样,前缀不同没有关系。SOAP信封元素是表示SOAP消息的XML文档的顶级元素。这个元素至少定义了SOAP命名空间,1.1版本的是http://schemas.xmlsoap.org/soap/envelope而1.2版本的是http://www.w3.org/ 2002/06/soap-envelope。命名空间用来消除SOAP标识符与应用定义的标识符之间可能存在的歧义。
- 标题(env:Header)
能够被SOAP消息路径中任意的SOAP接受者处理的一组SOAP条目,包括证明、路径信息、事务标识符等。这个元素是可选的。
- 内容(env:Body)
包含一组和多组SOAP条目的信息。这个元素必须放在header元素之后而且是必要的。
- 故障(env:Fault)
包含协议层错误信息的特殊SOAP条目,是可选的,但是如果出现必须放在body元素里。
图5-2所示为SOAP消息的结构,可以直接发送SOAP消息到接受者,也可以通过SOAP结点(Node)传送。如果标题中的一组或多组SOAP条目是给中间的SOAP结点使用的,SOAP结点在处理后,把它们去掉然后转送SOAP消息到接受者。如图5-3所示,在这个过程中,总是把未更改过的SOAP消息内容发送到接受者。
图5-2 SOAP消息的结构
图5-3 通过SOAP结点转送消息
除了Fault元素以外,SOAP没有规定Header和Body的具体内容,但是它提供了编码规则,使用env:encodingStyle元素定义如何把基本的数据类型转变成XML数据。SOAP 1.1版本的编码规则是http://schemas.xmlsoap.org/soap/encoding而1.2版本是http://www.w3.org/2002/06/soap-encoding。代码表5-4为定义的一个Java数据类型,代码表5-5为相应的SOAP编码XML消息。
代码表5-4 Employee.java数据类型
package com.acme;
public class Employee
{
int _id;
String _firstName;
String _lastName;
String _phone;
// getters and setters, etc.
...
}
|
代码表5-5 相应的SOAP编码XML消息
<?xml version='1.0' encoding='UTF-8'?>
<ns1:Employee
xmlns:ns1="http://tempuri.org/com.acme.TelephoneDirectory"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
xmlns:env="http://www.w3.org/2002/06/soap-envelope"
env:encodingStyle="http:// www.w3.org/2002/06/soap-encoding">
<id xsi:type="xsd:int">123</id>
<lastName xsi:type="xsd:string">Doe</lastName>
<firstName xsi:type="xsd:string">John</firstName>
<phone xsi:type="xsd:string">123-456-7890</phone>
</ns1:Employee>
|
SOAP标准规范只是定义了HTTP绑定,描述SOAP如何在HTTP POST请求中传输,没有定义如何使用其他,比如HTTP GET的传输方法。
SOAP使用命名空间做版本管理(Version Control)。如果SOAP 1.2应用程序收到一个SOAP 1.1请求消息,它的Envelope元素有一个与http://www.w3.org/2002/06/ soap-envelope不同的命名空间,则该应用程序可以按SOAP 1.1处理请求或者必须视它为一个版本错误并生成一个VersionMismatch SOAP错误消息。SOAP 1.2提供一个可选的Upgrade元素,让SOAP结点指定它支持哪个版本。代码表5-6为一个VersionMismatch SOAP错误消息和upgrade元素使用的示例。
代码表5-6 VersionMismatch SOAP错误消息和Upgrade元素使用的示例
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header>
<upg:Upgrade
xmlns:upg="http://www.w3.org/2002/06/soap-upgrade">
<envelope qname="ns1:Envelope"
xmlns:ns1=http://www.w3.org/2002/06/soap-envelope/>
</upg:Upgrade>
</env:Header>
<env:Body>
<env:Fault>
<faultcode>env:VersionMismatch</faultcode>
<faultstring>Requires SOAP 1.2</faultstring>
</env:Fault>
</env:Body>
</env:Envelope>
|
SOAP Fault元素用于在SOAP消息中传输错误或状态信息,它为不同的SOAP实现程序提供一个最基本的传输错误处理机制。Fault元素必须在Body元素内而且至多出现一次,它定义了下面的4个子元素:
- faultcode是必要的元素,标识错误代码方便程序处理。
- faultstring也是必要的元素,为错误代码提供一个程序员可以读懂的错误解释,它不是为程序处理而设。
- faultactor用于指示错误源,它的值是一个标识该源的URI。所有非SOAP消息最终接收者的应用程序必须包含此元素。
- detail提供更多的相关应用程序特别的错误信息。如果Body元素中的内容在被处理时出现错误,它必须出现,否则将不能使用它。
SOAP定义了以下的faultcode值:
- VersionMismatch:处理程序发现在Envelope元素中有一个非法的命名空间。
- MustUnderstand:Header元素的一个直接子元素无法被理解或者不遵守由处理对象要求的SOAP mustUnderstand属性必须取值为"1"的要求。
- DataEncodingUnkown:SOAP结点不认可收到的消息的编码规则。
- Sender:指示错误由客户机(Client)引起,例如消息的格式有误或消息中缺乏能成功处理所必须的一些适当信息。
- Receiver:表明消息无法被处理的原因不是由于消息本身错误引起,而是服务器(Server)当时所处的状态或处理错误导致的。
SOAP消息编码规则
接下来将介绍重要的编码规则,如果需要所有规则的详细说明,请参考标准规范。由于SOAP没有明确地定义编码与编程语言的映射,所以两个不同的SOAP实现对同一个对象很可能使用不同的编码。最基本的规则是所有的"value"值都使用XML元素而不是属性来编码。例如,正确的"value"值编码是:
<id>123</id>
而不是使用下面的方法:
<person id="123" />
SOAP支持的简单XML类型如下:
- string
- base64Binary
- interger, byte, short, int, long
- decimal, float, double
- boolean
- dateTime, time, date, duration
这些简单类型都是使用|XML元素来编码的,如果有需要,还可以用xsi:type属性来区分相似的类型。例如,可以这样来编码日期类型:
<ns1:test xmlns:ns1="www.test.com/test"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xsi:type="xsd:dateTime">
2002-07-18T12:00:00Z
</ns1:test>
|
由于不同的编程语言对xsi:type属性有不同的需求,这是各种SOAP实现不能互操作的主要原因。下面介绍两种复合类型:结构(Structs)和数组(Arrays)。结构是一个复合值,依靠存取标识(Accessor)区分其成员,而数组是一个复合值,在其成员值之间仅有顺序位置不同之分。代码表5-7为一个结构编码的示例。
代码表5-7 struct编码示例
<?xml version='1.0' encoding='UTF-8'?>
<ns1:Employee
xmlns:ns1="http://tempuri.org/com.acme.TelephoneDirectory"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:env="http://www.w3.org/2002/06/soap-envelope"
env:encodingStyle="http://www.w3.org/2002/06/soap-encoding">
<id xsi:type="xsd:int">123</id>
<lastName xsi:type="xsd:string">Doe</lastName>
<firstName xsi:type="xsd:string">John</firstName>
<phone xsi:type="xsd:string">123-456-7890</phone>
</ns1:Employee>
|
SOAP数组被定义为类型是enc:Array或类型源于enc:Array。SOAP数组必须包含一个enc:arrayType属性,其中定义的包含元素的值的类型与维数一起描述了该数组,它的定义是type[size],这和Java很相似。例如,一组3个string使用enc:arrayType表达是xsd:string[3],一组5个integer的表达是xsd:integer[5],而多维数组使用xsd:string[8][8]格式来表达。SOAP数组有一或多维,而它的成员由顺序位置区分。一个数组的值被表示为一序列反映该数组的元素,这些成员按序数从小到大顺序出现。对于多维数组,则元素维按从右到左顺序变化。代码表5-8为一个string数组编码的示例。
代码表5-8 array编码示例
<?xml version='1.0' encoding='UTF-8'?>
<ns1:array xmlns:ns1="http://www.test.com/test"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:enc="http://www.w3.org/2002/06/soap-encoding"
xsi:type="enc:Array"
enc:arrayType="xsd:string[3]">
<name xsi:type="xsd:string">A</name>
<name xsi:type="xsd:string">B</name>
<name xsi:type="xsd:string">C</name>
</ns1:array>
|
在传送多维数组的时候,为了提高性能可以只传送部分或者所需的数组值。SOAP数组成员可以包含一个enc:offset属性来指明该成员在其装载的数组中的偏移量。这也可以用于指明在一个部分描述的数组中成员的偏移。enc:offset属性的作用是方便传送部分数组值。代码表5-9为一个传送部分数组值的示例。
代码表5-9 传送部分数组值的示例
<?xml version='1.0' encoding='UTF-8'?>
<ns1:array xmlns:ns1="http://www.test.com/test"
xmlns:enc="http://www.w3.org/2002/06/soap-encoding"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
xsi:type="enc:Array"
enc:arrayType="xsd:int[8]"
enc:offset="[3]">
<id xsi:type="xsd:string">4</id>
<id xsi:type="xsd:string">5</id>
<id xsi:type="xsd:string">6</id>
<id xsi:type="xsd:string">7</id>
<id xsi:type="xsd:string">8</id>
</ns1:array>
|
类似enc:offset属性,SOAP数组成员可以包含一个enc:position属性来指明该成员在其装载的数组中的位置。这也可以用于指明在一个稀疏描述的数组中成员的位置。enc:offset和enc:position的基数都是0。enc:offset属性的作用是方便传送稀疏的数组。代码表5-10为一个传送稀疏的数组的示例。
代码表5-10 传送稀疏的数组的示例
<?xml version='1.0' encoding='UTF-8'?>
<ns1:array xmlns:ns1="http://www.test.com/test"
xmlns:enc="http://www.w3.org/2002/06/soap-encoding"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
xsi:type="enc:Array"
enc:arrayType="xsd:string[10, 10]">
<item xsi:type="xsd:string" enc:position="[1,1]">Item 1-1</item>
<item xsi:type="xsd:string" enc:position="[9,9]">Item 9-9</item>
</ns1:array>
|
总 结
到目前为止,我们已经讲述了SOAP的架构、基本技术知识、在Web服务应用上常用的技术, 接下来,在
调用Web服务系列的第二部分,我们将详细讨论如何使用SOAP来调用Web服务。
参考资料
-
《动态电子商务的Web服务》一书即将由清华大学出版社于2002年11月出版。该书描述了有关动态电子商务的Web服务的概念和理论,并且显示了相应的程序源码和样本应用,演示了如何使用IBM的WSAD开发工具来创建Web服务和基于Web服务的J2EE应用。以多个有代表性的、基于Web服务应用的具体案例,来详细说明使用Java的实施过程以及在实施过程中应注意的事项。通过基础理论、场景示例和应用程序示例来介绍和解释Web服务的技术,包括:WSDL、SOAP、UDDI、WSFL、WSIF等。本书针对广大的中国软件开发者而写,内容深入浅出,理论结合实际,有较强的实用性。在
《动态电子商务的Web服务》网页上包括内容简介、完整的目录以及定购该书的方法。
- 《动态电子商务的Web服务》一书第一章:
动态电子商务的Web服务:电子商务的演变
-
将应用程序的功能封装成为Web Services一文介绍了如何用IBM的开发工具WebSphere Studio Application Developer(WSAD)来定义、发布、定位和调用Web服务。
- 在developerWorks的
Web services专区查找更多的资料。
关于作者  | |  | 李劲, IBM软件解决方案多伦多实验室应用开发技术中心的部门负责人,他负责从收集用户要求到软件实现及测试的全部设计周期,即收集、分析用户的要求,并在软件以及WebSphere应用开发工具和面向Web应用的交互式设计技术,实现用户的要求和使用方案。 Jin目前的主要工作是Web服务和B2B应用集成,他具有十多年的软件业从业经验,曾多次使用VisualAge实现了IBM的Java和WebSphere应用服务器的客户承诺. 可以通过
jinli@ca.ibm.com与他联系.
|
对本文的评价
|