獨立元素
在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的熟悉程度,以及你的目標平臺的成熟度,你所花費的時間會有所不同。
新聞熱點
疑難解答