独立元素
在soap中,一个独立元素表示至少被一个多引用存取元素引用的类型的实例。所有的独立元素用soap:id属性作标记,而且这个属性的值在整个soap envelope中必须是唯一的。独立的元素被编码就好象是它们被一个存取元素打包,这个存取元素的标记名是实例的名域限制的类型名。在上面的例子中,实例的名域限制的类型名是t:adjustment。
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="#id1" />
<field2 soap:id="id1">hello, soap</field2>
</t:mytype>
尽管事实是存取元素2有一个soap:id属性,它实际上是一个存取元素而不是独立元素。
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="#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规范的最新版本来获得更多的细节。
错误处理
一个服务器有时将不能正确地为一个方法请求提供服务。这可能是由于一般的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的熟悉程度,以及你的目标平台的成熟度,你所花费的时间会有所不同。
新闻热点
疑难解答