SOAP详解

]和[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头的。这个头只在请求消息中需要,在响应消息中并不需要。

答者:qiri07(俺家金毛de地位比俺高) 信誉:99 级别:user4 日期:2006-10-22 10:36:06

第二节 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) 


这里,在发送请求消息之前,参数被序列化成为一个请求对象。同样被响应消息接收到的响应对象被反序列化为参数。一个类似的转变同样发生在调用的服务器端。 

当通过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>  
在请求的情况下,类型是隐式的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:Envelope> 
SOAP软件遇到不能识别必须的头元素情况时,必须拒绝这个消息并出示一个错误。如果服务器在一个SOAP请求中发现一个不能识别的必须的头元素,它必须返回一个错误响应并且不发送任何调用到目标对象。如果客户端在一个SOAP请求中发现一个不能识别出的必须的头元素,它必须向调用者返回一个运行时错误。在COM情况下,这将映射为一个明显的HRESULT。

答者:qiri07(俺家金毛de地位比俺高) 信誉:99 级别:user4 日期:2006-10-22 10:36:24

第三节 SOAP数据类型 

在SOAP消息中,每个元素可能是一个SOAP结构元素、根元素、存取元素或一个独立的元素。在SOAP中,soap:Envelope、soap:Body和soap:Header是唯一的组成元素。它们的基本关系由下列XML Schema所描述:  

<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> 
在这个例子中,存取元素account和amount被称着简单存取元素。对引用简单类型的存取元素,元素值被简单地编码为直接在存取元素下的字符数据,如上所示。对引用组合类型的存取元素(就是那些自身用子存取元素来构造的存取元素),有两个技术来对存取元素进行编码。最简单的方法是把被结构化的值直接嵌入在存取元素下。考虑下面的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''/> 
</type> 
在一个元素的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语言: 

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); 
} 
为了支持必须保持身份关系的类型的序列化,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> 

 
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值