首頁(yè) > 開(kāi)發(fā) > 綜合 > 正文

SOAP凈化有線(xiàn)協(xié)議(一):SOAP基礎知識

2020-02-03 15:08:22
字體:
來(lái)源:轉載
供稿:網(wǎng)友
一、soap簡(jiǎn)介
如前所述,soap用xml作為數據編碼格式。用xml作為數據編碼格式并非soap的原創(chuàng ),實(shí)際上這是一種相當自然的選擇。xml-rpc和ebxml也同樣使用xml。要了解這方面的更多信息,請參見(jiàn)本文最后的“參考資源”。

請考慮下面的java接口:

listing 1

public interface hello
{
public string sayhelloto(string name);
}
客戶(hù)程序在調用sayhelloto()方法時(shí)提供了一個(gè)名字,它希望從服務(wù)器接收到一則個(gè)性化的“hello”信息?,F在,假定rmi、corba和dcom都不存在,開(kāi)發(fā)者必須負責串行化方法調用,并把消息發(fā)送給遠程機器。幾乎所有的人都會(huì )說(shuō)“這該使用xml”,我同意。因此,讓我們先從對服務(wù)器的請求格式開(kāi)始。假設要模擬sayhelloto("john")調用,我打算發(fā)送的請求是:

listing 2

<?xml version="1.0"?>
<hello>
<sayhelloto>
<name>john</name>
</sayhelloto>
</hello>
在這里,我把接口的名字作為根結點(diǎn)。另外,我還把方法名字和參數名字都當作節點(diǎn)。接下來(lái),我們要把這個(gè)請求發(fā)送給服務(wù)器。我們不創(chuàng )建自己的tcp/ip消息,而是使用http。因此,下面的步驟應該是把請求封裝成http post請求格式,然后把它發(fā)送給服務(wù)器。實(shí)際創(chuàng )建該http post請求的詳細過(guò)程在本文后面介紹,現在,我們先假定它已經(jīng)創(chuàng )建完畢。服務(wù)器接收到了這個(gè)請求,解碼xml,然后再以xml格式向客戶(hù)程序發(fā)送應答。假設應答內容如下:

listing 3

<?xml version="1.0"?>
<hello>
<sayhellotoresponse>
<message>hello john, how are you?</message>
</sayhellotoresponse>
</hello>
根節點(diǎn)仍然是接口的名字hello。但這一次,原來(lái)對應著(zhù)方法的節點(diǎn)名字不再是sayhelloto,而是方法的名字加上“response”字符串??蛻?hù)程序知道自己調用了哪一個(gè)方法,要找出被調用方法的返回值,它只需查看名字為方法名字加上“response”字符串的元素。

以上就是soap的根本思路。listing 4顯示了同一請求用soap xml編碼之后的結果:

listing 4

<soap-env:envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/1999/xmlschema-instance" xmlns:xsd="http://www.w3.org/1999/xmlschema">
<soap-env:header>
</soap-env:header>
<soap-env:body>
<ns1:sayhelloto
xmlns:ns1="hello"
soap-env:encodingstyle="
http://schemas.xmlsoap.org/soap/encoding/">
<name xsi:type="xsd:string">john</name>
</ns1:sayhelloto>
</soap-env:body>
</soap-env:envelope>
看起來(lái)稍微復雜了一點(diǎn),對吧?實(shí)際上,它和我們前面編寫(xiě)的請求類(lèi)似,只是略微擴展了一些東西。首先,注意soap文檔通過(guò)一個(gè)envelope(根節點(diǎn))、一個(gè)header區、一個(gè)body區,整潔地組織到一起。header區用來(lái)封裝那些與方法本身無(wú)直接關(guān)系的數據,提供環(huán)境方面的信息,比如事務(wù)id和安全信息。body區包含面向方法本身的信息。在listing 2中,我們自己編寫(xiě)的xml只包含一個(gè)body區。

第二,注意listing 4大量地應用了xml名稱(chēng)空間。soap-env映射到名稱(chēng)空間http://schemas.xmlsoap.org/soap/envelope/,xsi映射到http://www.w3.org/1999/xmlschema-instance,而xsd映射到http://www.w3.org/1999/xmlschema。這三者是所有soap文檔都擁有的標準名稱(chēng)空間。

最后,在listing 4中,接口名稱(chēng)(即hello)不再象在listing 2中那樣成為節點(diǎn)的名字;相反,它引用了一個(gè)名稱(chēng)空間nsl。另外,參數的類(lèi)型信息也隨同參數的值一起發(fā)送給了服務(wù)器。注意信封(envelope)encodingstyle屬性的值。這個(gè)屬性值設置成了http://schemas.xmlsoap.org/soap/encoding/。這個(gè)值告訴服務(wù)器用來(lái)編碼(即串行化)方法的編碼方式;服務(wù)器需要這個(gè)信息,以便正確地解除方法的串行化。對于服務(wù)器來(lái)說(shuō),soap文檔的自我描述能力是相當完善的。

對于上面的soap請求,服務(wù)器的應答如下:

listing 5

<soap-env:envelope
xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/xmlschema-nstance"
xmlns:xsd="http://www.w3.org/1999/xmlschema">
<soap-env:body>
<ns1:sayhellotoresponse
xmlns:ns1="hello"
soap-env:encodingstyle="http://schemas.xmlsoap.org/soap/encoding/">
<return xsi:type="xsd:string">hello john, how are you doing?</return>
</ns1:sayhellotoresponse>
</soap-env:body>
</soap-env:envelope>
listing 5與listing 4的請求消息類(lèi)似。在上面的代碼中,返回值(即個(gè)性化的“hello”消息)包含在body區。soap消息文檔的格式非常靈活。例如,編碼方式不固定,而是由客戶(hù)程序指定。只要是客戶(hù)程序和服務(wù)器都認可的編碼方式,可以是任何合法的xml文檔。

另外,分離調用環(huán)境信息意味著(zhù)方法本身并不關(guān)心這類(lèi)信息。在當前的市場(chǎng)上,主流應用服務(wù)器都遵從這一理念。早先,我曾經(jīng)指出環(huán)境信息可以包含事務(wù)和安全方面的信息。事實(shí)上,環(huán)境可以涵蓋幾乎所有的東西。下面是一個(gè)soap消息頭的例子,它帶有一些事務(wù)方面的信息:

listing 6

<soap-env:header>
<t:transaction xmlns:t="some-uri" soap-env:mustunderstand="1">
5
</t:transaction>
</soap-env:header>
名稱(chēng)空間t映射到了與特定應用有關(guān)的uri。這里的5表示的是該方法從屬于其中的事務(wù)id。注意soap信封mustunderstand屬性的應用。這個(gè)屬性被設置成了1,它表示服務(wù)器要么理解并按照要求處理該事務(wù)請求,要么表示無(wú)法處理該請求;這是soap規范所要求的。

二、錯誤處理
使用soap并不意味著(zhù)任何時(shí)候所有的請求都會(huì )獲得成功。許多地方可能會(huì )出現差錯。例如,服務(wù)器可能無(wú)法訪(fǎng)問(wèn)某個(gè)關(guān)鍵性的資源(比如數據庫),因而無(wú)法順利地處理請求。

讓我們返回“hello”實(shí)例,為它加上一個(gè)假想的約束,即“在星期二向別人說(shuō)hello不合法?!币虼?,星期二的時(shí)候,即使發(fā)送給服務(wù)器的請求是合法的,服務(wù)器也會(huì )把一個(gè)錯誤信息返回給客戶(hù)端。應答內容將如下所示:

listing 7

<soap-env:envelope xmlns:soap-env="
http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:body>
<soap-env:fault>
<faultcode>soap-env:server</faultcode>
<faultstring>server error</faultstring>
<detail>
<e:myfaultdetails xmlns:e="hello">
<message>
sorry, my silly constraint says that i cannot say hello on tuesday.
</message>
<errorcode>
1001
</errorcode>
</e:myfaultdetails>
</detail>
</soap-env:fault>
</soap-env:body>
</soap-env:envelope>
我們來(lái)分析一下http://schemas.xmlsoap.org/soap/envelope/名稱(chēng)空間定義的fault元素。fault元素總是body元素的直接子元素,所有的soap服務(wù)器必須始終通過(guò)該元素報告所有錯誤情況。fault元素必須包含faultcode和faultstring元素,不能有例外。faultcode是一個(gè)能夠標識問(wèn)題的代碼;客戶(hù)程序按照soap規范的要求利用faultcode進(jìn)行算法處理。soap規范定義了一小組錯誤代碼供用戶(hù)使用。另一方面,faultstring是供人類(lèi)閱讀的錯誤信息。

listing 7的代碼還包含了一個(gè)detail元素。由于錯誤在處理soap消息的body區時(shí)出現,detail元素必須出現。正如你將在本文后面看到的,如果錯誤在處理header區時(shí)出現,detail元素不應出現。在listing 7中,應用利用detail元素提供當前錯誤更詳細、更自然的解釋?zhuān)葱瞧诙辉试S說(shuō)hello。soap還提供另外一個(gè)面向具體應用的錯誤代碼,即半可選的faultfactor元素,但上面的錯誤信息中沒(méi)有顯示這個(gè)元素。之所以稱(chēng)這個(gè)元素是半可選的,是因為如果錯誤消息不是由請求最終處理點(diǎn)的服務(wù)器發(fā)送,即由一個(gè)中間服務(wù)器發(fā)送,則錯誤消息必須包含該元素。soap對faultcode元素不應出現的情形沒(méi)有作任何規定。

在listing 7中,錯誤起源于方法調用本身,處理該方法的應用導致了這個(gè)錯誤?,F在,我們來(lái)看一下另一種類(lèi)型的錯誤,這種錯誤由于服務(wù)器不能處理請求頭信息而導致。舉例來(lái)說(shuō),假設所有的hello消息必須在一個(gè)事務(wù)環(huán)境之內生成,則請求類(lèi)似于:

listing 8
<soap-env:envelope
xmlns:soap-env="
http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="
http://www.w3.org/1999/xmlschema-instance"
xmlns:xsd="http://www.w3.org/1999/xmlschema">
<soap-env:header>
<t:transaction xmlns:t="some-uri"
soap-env:mustunderstand="1">
5
</t:transaction>
</soap-env:header>
<soap-env:body>
<ns1:sayhelloto
xmlns:ns1="hello"
soap-env:encodingstyle="
http://schemas.xmlsoap.org/soap/encoding/">
<name xsi:type="xsd:string">tarak</name>
</ns1:sayhelloto>
</soap-env:body>
</soap-env:envelope>
上面消息的header區包含一個(gè)transaction元素,它指定了方法調用必須從屬于其中的事務(wù)編號。這里我說(shuō)“必須”是因為transaction元素使用了mustunderstand屬性。如前所述,soap服務(wù)器要么遵照屬性的指示處理請求,要么聲明不能處理請求。假定soap服務(wù)器不能處理,它必須返回一個(gè)錯誤信息。這時(shí)的應答應該類(lèi)似于:

listing 9
<soap-env:envelope xmlns:soap-env="
http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:body>
<soap-env:fault>
<faultcode>soap-env:mustunderstand</faultcode>
<faultstring>soap must understand
error</faultstring>
</soap-env:fault>
</soap-env:body>
</soap-env:envelope>
上面的代碼類(lèi)似listing 7顯示的錯誤信息。但應該注意的是,detail元素不再出現。正如我在前面指出的:soap規范規定,如果錯誤在處理header區的時(shí)候出現,則錯誤消息中不應包含detail元素。事實(shí)上,我們可以根據detail元素是否出現,迅速判定錯誤是在處理body區還是在處理header區時(shí)出現。

三、soap與http
在第一個(gè)例子中,我通過(guò)http把定制的xml請求發(fā)送給服務(wù)器,但沒(méi)有詳細介紹這么做涉及到了哪些操作?,F在我們回過(guò)頭來(lái)看那個(gè)問(wèn)題。怎樣才能把一個(gè)soap請求(而不是定制的xml)通過(guò)http發(fā)送給服務(wù)器?soap很自然地遵循了http的請求/應答消息模型。這個(gè)模型在http請求中提供soap請求參數,在http應答中提供soap應答參數。實(shí)際上,soap 1.0特別指明http作為它的傳輸協(xié)議。soap 1.1略有放松。雖然soap 1.1仍舊可以使用http,但它也可以使用其他協(xié)議,比如smtp。在這個(gè)系列的文章中,我只討論通過(guò)http使用soap的情形。

讓我們返回hello示例。如果我們通過(guò)http把soap請求發(fā)送給服務(wù)器,則代碼應該類(lèi)似于:

listing 10
post http://www.smarthello.com/helloapplication http/1.0
content-type: text/xml; charset="utf-8"
content-length: 587
soapaction: "http://www.smarthello.com/helloapplication#sayhelloto"
<soap-env:envelope
xmlns:soap-env="
http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="
http://www.w3.org/1999/xmlschema-instance"
xmlns:xsd="http://www.w3.org/1999/xmlschema">
<soap-env:header>
</soap-env:header>
<soap-env:body>
<ns1:sayhelloto
xmlns:ns1="hello"
soap-env:encodingstyle="
http://schemas.xmlsoap.org/soap/encoding/">
<name xsi:type="xsd:string">tarak</name>
</ns1:sayhelloto>
</soap-env:body>
</soap-env:envelope>

listing 10代表的soap請求與listing 4的請求基本相同,但listing 10的開(kāi)頭加入了一些http特有的代碼。第一行代碼表明這是一個(gè)遵循h(huán)ttp 1.1規范的post請求,post的目標是http://www.smarthello.com/helloapplication。下一行指示內容的類(lèi)型,在http消息中包含soap實(shí)體時(shí),內容類(lèi)型必須是text/xml。content-length指明了post請求有效載荷的長(cháng)度。

第四行是soap特有的,而且它是必不可少的。soapaction http請求頭指明了soap http請求的目標,它的值是一個(gè)標識目標的uri。soap不對該uri的格式作任何限制,實(shí)際上,這個(gè)uri甚至不必對應某個(gè)實(shí)際的位置。

soapaction的一個(gè)可能的應用是,防火墻檢查該請求頭的值,決定是否允許請求通過(guò)防火墻。

一旦服務(wù)器處理完請求,它將向客戶(hù)返回一個(gè)應答。應答的內容如listing 11所示(假設沒(méi)有出現錯誤):

listing 11
http/1.0 200 ok
content-type: text/xml; charset="utf-8"
content-length: 615
<soap-env:envelope
xmlns:soap-env="
http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="
http://www.w3.org/1999/xmlschema-instance"
xmlns:xsd="http://www.w3.org/1999/xmlschema">
<soap-env:body>
<ns1:sayhellotoresponse
xmlns:ns1="hello"
soap-env:encodingstyle="
http://schemas.xmlsoap.org/soap/encoding/">
<return xsi:type="xsd:string">hello john, how are
you doing?</return>
</ns1:sayhellotoresponse>
</soap-env:body>
</soap-env:envelope>


這個(gè)soap應答與listing 5所顯示的一樣,但前面加上了一些http特有的代碼。由于沒(méi)有出現錯誤,第一行代碼顯示應答狀態(tài)是200。在http協(xié)議中,200應答狀態(tài)代碼表示“一切正?!?。如果在處理soap消息(header區或者body區)的時(shí)候出現了任何錯誤,則返回的狀態(tài)代碼將是500。在http中,500狀態(tài)代碼表示“internal server error”。此時(shí),上述soap應答的第一行代碼將是:

http 500 internal server error
四、http擴充框架
許多應用對服務(wù)的需求超過(guò)了傳統http提供的服務(wù)。其結果就是,這類(lèi)應用擴充了傳統的http協(xié)議。然而,這種擴充是應用本身私有的。http擴充框架試圖確立一個(gè)通用的http擴充機制,從而解決這個(gè)問(wèn)題。http擴充框架的擴充之一是增加了一個(gè)m-post方法,其中的m表示mandatory(必須遵循的,強制的)。如果一個(gè)http請求包含至少一個(gè)強制的擴充聲明,那么這個(gè)請求就稱(chēng)為強制的請求。引入強制的擴充聲明通過(guò)man或者c-man頭進(jìn)行。強制請求的請求方法名字必須帶有“m-”前綴,例如,強制的post方法稱(chēng)為m-post。

soap 1.0要求客戶(hù)程序首先發(fā)送一個(gè)http post請求,只有當服務(wù)器返回錯誤510時(shí)才發(fā)送m-post請求。soap 1.1不再對客戶(hù)作這種限制,也就是說(shuō),soap 1.1允許客戶(hù)從發(fā)送任何一種類(lèi)型的請求開(kāi)始。下面的請求就是迄今為止我們一直在討論的那個(gè)請求,但它現在是m-post格式:

listing 12
m-post http://www.smarthello.com/helloapplication http/1.1
content-type: text/xml; charset="utf-8"
content-length: 587
man: "http://schemas.xmlsoap.org/soap/envelope/"; ns=01
01-soapaction: "http://www.smarthello.com/helloapplication#sayhelloto"
<soap-env:envelope
xmlns:soap-env="
http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="
http://www.w3.org/1999/xmlschema-instance"
xmlns:xsd="http://www.w3.org/1999/xmlschema">
<soap-env:header>
</soap-env:header>
<soap-env:body>
<ns1:sayhelloto
xmlns:ns1="hello"
soap-env:encodingstyle="
http://schemas.xmlsoap.org/soap/encoding/">
<name xsi:type="xsd:string">tarak</name>
</ns1:sayhelloto>
</soap-env:body>
</soap-env:envelope>
對于實(shí)際的soap消息來(lái)說(shuō),listing 12和listing 10沒(méi)有什么不同。但請求頭中有一些不同的地方,例如,現在我們發(fā)出的不是post請求,而是一個(gè)m-post請求。正如前面所介紹的,象m-post這樣的強制請求至少有一個(gè)強制擴充聲明。這里我們就有一個(gè):man域描述了一個(gè)強制性的端到端擴充聲明,把頭前綴01映射到了名稱(chēng)空間http://schemas.xmlsoap.org/soap/envelope/。請注意這個(gè)前綴關(guān)聯(lián)到soapaction域的方式。

一旦服務(wù)器處理完該請求,它將返回一個(gè)應答給客戶(hù)。應答內容類(lèi)如(假設沒(méi)有出現錯誤):

listing 13
http/1.0 200 ok
ext:
content-type: text/xml; charset="utf-8"
content-length: 615
<soap-env:envelope
xmlns:soap-env="
http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="
http://www.w3.org/1999/xmlschema-instance"
xmlns:xsd="http://www.w3.org/1999/xmlschema">
<soap-env:body>
<ns1:sayhellotoresponse
xmlns:ns1="hello"
soap-env:encodingstyle="
http://schemas.xmlsoap.org/soap/encoding/">
<return xsi:type="xsd:string">hello john, how are
you doing?</return>
</ns1:sayhellotoresponse>
</soap-env:body>
</soap-env:envelope>
同樣地,listing 13顯示的應答類(lèi)似于對普通post請求的應答(如listing 11所示),兩者的不同之處在于ext域。

在通過(guò)http使用soap的過(guò)程中,我們欣喜地看到,實(shí)際的soap消息(soap信封和它里面的所有內容)總是保持不變,就如消息尚未加載http協(xié)議時(shí)一樣。根據這一事實(shí)可以推斷出,http不是能夠與soap協(xié)作的唯一協(xié)議。例如,soap可以方便地和smtp協(xié)議或者其他定制的私有協(xié)議一起運行。唯一的要求是兩者——客戶(hù)端和服務(wù)器端——都理解該協(xié)議。

五、soap的特點(diǎn):簡(jiǎn)單
至此為止,我們討論了soap定義的方方面面,但有許多領(lǐng)域的問(wèn)題soap沒(méi)有定義。soap規范的創(chuàng )立者明確地排除了一些關(guān)系密切的領(lǐng)域,比如構造對象模型,還有其他許多已經(jīng)確立的標準。

造成這種現象的原因可以從分析soap的目標理解。soap的目標除了擴展性之外,另一個(gè)主要的設計目標是簡(jiǎn)單。為了保持soap簡(jiǎn)單,soap規范的創(chuàng )立者決定,只定義那些對于創(chuàng )建一個(gè)輕型協(xié)議來(lái)說(shuō)絕對必須的東西。例如,soap沒(méi)有定義/指定任何有關(guān)分布式垃圾收集、類(lèi)型安全或版本控制、雙向http通信、消息盒(message-box)運輸或管道處理、對象激活等方面的內容。soap的目標就是成為一種簡(jiǎn)單的協(xié)議——一種在任何操作系統上,單個(gè)開(kāi)發(fā)者能夠用任何語(yǔ)言化數天時(shí)間實(shí)現的協(xié)議??紤]到這一點(diǎn),soap在許多方面沒(méi)有作出明確定義實(shí)際上是一件好事,因為在構造分布式系統時(shí),所有現有的技術(shù)都可以方便地采用soap,即使不同技術(shù)之間的差異象corba和dcom的差異那樣明顯。國內最大的酷站演示中心!
發(fā)表評論 共有條評論
用戶(hù)名: 密碼:
驗證碼: 匿名發(fā)表