簡易物件存取通訊協定 (SOAP)(SOAP)初級指南

來源:互聯網
上載者:User
對象|訪問



總結:
(本文假設讀者對COM和XML技術已經很熟悉。)
SOAP(Simple Object Access Protocal) 技術有助於實現大量異構程式和平台之間的互通性,從而使存在的應用能夠被廣泛的使用者所訪問。SOAP是把成熟的基於HTTP的WEB技術與XML的靈活性和可擴充性組合在了一起。
這篇文章帶你全面回顧對象遠程進程調用(ORPC)技術的曆程,以協助你理解SOAP技術的基礎,以及它克服存在技術(如CORBA和DCOM)的許多缺陷的方法。隨後講述詳細的SOAP編碼規則,並把焦點放在SOAP是怎樣映射到存在的ORPC概念上的。
引言:
當我在1984年開始把計算作為我的職業的時候,大多數程式員並不關心網路通訊協定。但是在九十年代網路變得無所不在,現在如果有誰使用電腦卻不使用某種形式網路連接是很難以想象的。今天,一般的程式員對建立可擴充的分布式應用表現出更大的興趣,而不再只是關注於用MFC實現個人化的可浮動半透明非矩形的Coolbars了。
程式員通常喜歡用編程模型來思考問題,而很少考慮網路通訊協定。儘管這樣做通常是很好的,但在這篇文章中我將討論的SOAP是一個沒有明顯的編程模型的網路通訊協定。這並不意味著SOAP的體繫結構從根本上會改變你編程的方式。相反,SOAP的一個主要目標是使存在的應用能被更廣泛的使用者所使用。為了實現這個目的,沒有任何SOAP API或SOAP 對象請求代理(SOAP ORB),SOAP是假設你將使用儘可能多的存在的技術。幾個主要的CORBA廠商已經承諾在他們的ORB產品中支援SOAP協議。微軟也承諾在將來的COM版本中支援SOAP。DevelopMentor已經開發了參考實現,它使得在任何平台上的任何Java或Perl程式員都可以使用SOAP。
在SOAP後面的指導理念是“它是第一個沒有發明任何新技術的技術”。SOAP採用了已經廣泛使用的兩個協議:HTTP和XML。HTTP用於實現SOAP的RPC風格的傳輸,而XML是它的編碼模式。採用幾行代碼和一個XML解析器,HTTP伺服器(如MS的IIS或Apache)立刻成為了SOAP的ORBs。 因為目前超過一半的Web伺服器採用IIS或Apache, SOAP將會從這兩個產品的廣泛而可靠的使用中擷取利益。這並不意味著所有的SOAP請求必須通過Web伺服器來路由,傳統的Web 服務器只是指派SOAP請求的一種方式。因此Web服務如IIS或Apache對建立SOAP使能的應用是充分的,但決不是必要的。
正如這篇文章將要描述的,SOAP簡單地用XML來編碼HTTP的傳輸內容。SOAP最常執行的 App是作為一個RPC協議。為了理解SOAP怎樣工作,有必要簡要回顧一下RPC協議的曆史。
RPCs的曆史
建立分布式應用的兩個主要通訊模型是訊息傳送(經常與隊列組合在一起)和請求/響應。訊息傳遞系統允許通訊任何一方在任何時間發送訊息。請求/響應協議把通訊模式限制在請求/響應的雙方。基於訊息的應用強烈地意識到它們正在與外部的並行進程進行通訊,並且需要一個顯式的設計風格。基於請求/響應的應用更象一個單進程的應用,因為發送請求的應用或多或少被阻塞直至收到來自另一個進程的響應。這使得請求/響應通訊自然地適合於RPC應用。
儘管訊息通訊和請求/響應各有他們的優點,他們都是可以用對方來實現的。訊息系統可以用較底層的請求/響應協議來建立。如微軟的Message Queue Server (MSMQ)內部採用了DCE RPC來建立大多數的控制邏輯。RPC系統也可以採用較底層的訊息系統來建立。MSMQ提供的關聯 ID正是為了這個目的。不管評價如何,大多數的應用仍趨向於使用RPC協議,因為它們廣泛的使用,它們更簡單的設計,以及更自然的到傳統的編程技術的映射。
在八十年代,兩個主要的RPC協議是Sun RPC 和DCE RPC。最流行的Sun RPC應用是大多數UNIX系統所使用的Network File System (NFS)。最流行的DCE RPC應用則是Windows NT?,它採用DCE RPC 協議來實現許多系統服務。這兩個協議被證明適用於很大範圍的應用。但是,在八十年代末期,物件導向技術的風靡使軟體界沉迷於在物件導向語言和基於RPC的通訊之間建立一個紐帶。
在九十年代產生的對象RPC (ORPC) 協議正是試圖把物件導向和網路通訊協定聯絡起來。ORPC 和 RPC 協議的主要不同是ORPC代碼化了從通訊終端到語言級對象的映射。在每個ORPC請求的頭中都有一個cookie,伺服器端的程式能用它來定位在伺服器處理序中的目標對象。通常這個cookie只是一個對數組的索引,但其它技術也經常被使用,如用符號名作為Hash表的鍵。

圖1 ORPC請求與響應
圖1表示一個典型的ORPC請求和響應。有幾個要求標頭組件被伺服器端的處理常式用於分發調用。對象端點ID被用於定位在伺服器處理序中目標對象。介面標識符和方法標識符用於決定在目標對象中哪一個方法被調用。傳輸體用於傳遞請求中的[in]和[in,out]參數的值(在響應中是[out]和[in,out])。要注意的是任選的協議擴充可以出現在標頭檔和傳輸體之間。這是在協議設計中的慣例,因為它允許新的服務搭載在ORPC的請求和服務上。大多數ORPC系統用這個地區傳遞附加的上下文資訊(如事務資訊和因果關係標識符)。
目前兩個主要的OPRC協議是DCOM 和 CORBA的 Internet Inter-ORB Protocol (IIOP) 或更一般的General Inter-ORB Protocol (GIOP)。DCOM和IIOP/GIOP的請求格式非常相似。兩個協議都用一個對象端點ID來確定目標對象,用方法標識符來決定調用哪個方法。
這兩個協議主要有兩點不同:主要的一點不同是採用IIOP/GIOP時,介面標識符是隱含的,因為一個給定的CORBA對象只實現一個介面(儘管OMG當前進行中每個對象有多個介面支援的標準化工作)。DCOM與IIOP/GIOP請求的另一個細微差別是在傳輸體中參數值的格式。在DCOM中,傳輸體用網路資料表達(NDR)的格式來寫,在IIOP/GIOP中,傳輸體用公用資料表達(CDR)的格式來寫。NDR和 CDR分別處理在各種平台上的不同的資料表達。但是在這兩種格式之間有一些小的差別,這使它們相互之間並不相容。
在ORPC與RPC協議之間的另一個重要的不同是通訊端點的命名方式。在ORPC協議中,對於ORPC端點的一些可傳遞的表達方式被要求在網路之間傳遞對象引用。在CORBA/IIOP,這個表達方式被稱為可互動的對象引用(IOR)。IORs包含用緊湊格式表達的定址資訊,使用了它任何CORBA產品都可以決定一個對象端點。在DCOM中,這種表達方式被稱為OBJREF,它組合了分布的引用計算和端點/對象標識。CORBA和DCOM都提供了在網路上尋找對象端點的進階機制,但最終這些機制都映射回到了IORs或OBJREFs。圖3是表示一個 IOR/OBJREF 怎樣與在IIOP/DCOM請求訊息中的定址資訊關聯起來的。
目前的技術存在的問題?
儘管DCOM和IIOP都是固定的協議,業界還沒有完全轉向其中任何一個協議。沒有融合的部分原因是文化的問題所致。而且在當一些組織試表徵圖准化一個或另一個協議的時候,兩個協議的技術適用性就被提出質疑。傳統上認為DCOM和CORBA都是合理伺服器到伺服器端的通訊協定。但是,二者對客戶到伺服器端的通訊都存在明顯的弱點,尤其是客戶機被散布在Internet上的時候。
DCOM 和 CORBA/IIOP都是依賴於單個廠商的解決方案來最大優勢地使用協議。儘管兩個協議都在各種平台和產品上被實現了,但現實是選定的發布需要採用單一廠商的實現。在DCOM的情況下,這意味著每個機器要運行在Windows NT。(儘管DCOM已經被轉移到其它平台,但它只在Windows?上獲得了廣泛的延伸)。在CORBA情況下,這意味著每個機器要運行同樣的ORB產品。的確讓兩個CORBA產品用IIOP相互調用是有可能的,但是許多進階的服務(如安全和事務)此時通常不是可互動的。而且,任何專門廠商為同樣的機器的通訊所作的最佳化很難起作用,除非所有的應用被建立在同一個ORB產品上。
DCOM 和CORBA/IIOP都依賴於周密管理的環境。兩個任意的電腦使得DCOM或IIOP 在環境之外被成功調用(calls out of the box)的幾率是很低的。特別是在考慮安全性的時候尤其是這樣。儘管寫一個能成功地運用DCOM或IIOP的緊縮包(shrink-wrap)應用是可能的,但這樣做要比基於socket的應用要更多地關注細節。這對於乏味但必需的配置和安裝管理工作特別適用。
DCOM 和 CORBA/IIOP都依賴於相當高技術的運行環境。儘管進程內的COM似乎特別簡單,但COM/DCOM遠端程式絕對不只是幾天就解決的事情。IIOP 是一個比DCOM更容易實現的協議,但兩個協議都有相當多的深奧的規則來處理資料排列、類型資訊和位操作。這使得一般的程式員在沒有領會ORB產品或OLE32.DLL的情況下去構造一個簡單的CORBA或DCOM調用也變得很困難。
也許對DCOM和CORBA/IIOP來說,最令人難以忍受的一點是它們不能在Internet 上發揮作用。對DCOM來說,一般使用者的iMac 或廉價的運行Windows 95的PC 相容機要想使用你的伺服器執行基於領域認證幾乎是不可能的。更糟的是,如果防火牆或Proxy 伺服器分隔開了客戶和伺服器的機器,任何IIOP或DCOM包要通過的可能性是很低的,主要是由於大多數Internet串連技術對HTTP協議的偏愛所致。儘管一些廠商如Microsoft, Iona和Visigenic都已經建立了通道技術,但這些產品很容易對配置錯誤敏感而且它們是不可互動的。
在一個伺服器群落中這些問題並不能影響DCOM或IIOP的使用。因為在伺服器群落中主機的數量很少(一般是成百上千,而不是成千上萬),這就抵消了DCOM基於ping的生命週期管理的成本。在伺服器群落中,所有主機被一個公用管理域管理的機率很大,使得統一的配置變得可能。相對少量的機器也能保持商業ORB產品可控制使用的成本,因為只需要更少量的ORB許可權。如果只有IIOP在伺服器群落中被使用,就只需要少量的ORB許可權。最後,在伺服器群落中所有主機有直接的IP串連也是可能的,這就消除了與防火牆相關的DCOM和 IIOP問題。
 
HTTP作為一個更好的RPC
在伺服器群落中使用DCOM 和CORBA 是通用的做法,但客戶機則使用HTTP進入伺服器群落。HTTP與RPC的協議很相似,它簡單、配置廣泛,並且對防火牆比其它協議更容易發揮作用。HTTP請求一般由Web伺服器軟體(如IIS和Apache)來處理,但越來越多的應用伺服器產品正在支援HTTP作為除DCOM和IIOP外的又一個協議。
象DCOM和IIOP一樣,HTTP層通過TCP/IP進行請求/響應通訊。一個HTTP的用戶端用TCP串連到HTTP伺服器。在HTTP中使用的標準連接埠號碼是80,但任何其它連接埠也能被使用。在建立TCP串連後,用戶端可以發送一個請求訊息到伺服器端。伺服器在處理請求後發回一個HTTP響應訊息到用戶端。請求和響應訊息都可以包含任意的傳輸體的資訊,通常用Content-Length和Content-Type的 HTTP 頭來標記。下面是一個合法的HTTP請求訊息:
POST /foobar HTTP/1.1
Host: 209.110.197.12
Content-Type: text/plain
Content-Length: 12
Hello, World
你可能已經注意到HTTP頭只是一般文本。這使得用包檢查程式或基於文本的Internet工具(如telnet)來診斷HTTP問題變得更容易。HTTP基於文本的屬性也使得HTTP更容易適用於在Web開發中流行的低技術水平的編程環境。
HTTP請求的第一行包含三個組件:HTTP方法,請求-URI,協議版本。在前面的例子中,這些分別對應於POST, /foobar, 和 HTTP/1.1。Internet工程工作群組(IETF)已經標準化了數量固定的HTTP方法。GET是HTTP用來訪問Web的方法。 POST是建立應用程式的最常用的HTTP方法。和GET不一樣,POST允許任意資料從用戶端發送到伺服器端。請求URI (Uniform Resource Identifier)是一個HTTP伺服器端軟體,它用來識別請求的目標的簡單的標識符(它更象一個IIOP/GIOP object_key 或一個DCOM IPID)。關於URIs更多的資訊請參照"URIs, URLs, and URNs"。在這個例子中協議的版本是HTTP/1.1, 它表示遵守RFC 2616的規則。HTTP/1.1比HTTP/1.0多增加了幾個特性,包括對大塊資料轉送的支援以及對在幾個HTTP請求之間保持TCP串連的支援。
請求的第三行和第四行指定了請求體的尺寸和類型。Content-Length 頭指定了體資訊的位元數。Content-Type類型標識符指定MIME類型為體資訊的文法。HTTP (象 DCE一樣) 允許伺服器和用戶端協商用於編製資訊的傳輸文法。大多數DCE應用採用NDR.。大多數Web應用採用text/html 或其它基於文本的文法。
注意在上面範例中Content-Length頭與請求體之間的空行。不同的HTTP頭被carriage-return/行碼序列劃定界限。這些頭與體之間用另外的carriage-return/行碼序列來劃定界限。請求接著包括原始位元組,這些位元組的文法和長度由Content-Length和Content-Type HTTP 頭來識別。在這個例子中,內容是十二位元組的普通文本字串"Hello, World"。
在處理了請求之後,HTTP伺服器被期望發回一個HTTP響應到用戶端。響應必須包括一個狀態碼來表示請求的結果。響應也可以包含任意的體資訊。下面是一個HTTP響應訊息:
200 OK
Content-Type: text/plain
Content-Length: 12
dlroW ,olleH
在這個例子中,伺服器返回狀態碼200,它是HTTP中標準的成功碼。如果伺服器端不能破解請求代碼,它將返回下列的響應:
400 Bad Request
Content-Length: 0
如果HTTP伺服器決定到目標URI的請求應該臨時轉向另外的一個不同的URI,下列響將被返回:
307 Temporarily Moved
Location: http://209.110.197.44/foobar
Content-Length: 0
這個響應告知客戶,請求將能夠通過重新傳遞它到在Location頭中指定的地址來被滿足。
所有的標準狀態代碼和頭都在RFC 2616中被描述。它們中很少的內容與SOAP使用者直接相關,但有一個明顯的例外。在HTTP/1.1,底層的TCP串連在多個請求/響應對之間重用。HTTP Connection頭允許用戶端或伺服器中任何一方關閉底層的串連。通過增加下列HTTP頭到請求或響應中,雙方都會要求在處理請求後關閉它們的TCP串連:
Connection: close
當與HTTP/1.0軟體互動時,為了保持TCP串連,建議發送方加入下列HTTP頭到每個請求或響應中:
Connection: Keep-Alive
這個頭使預設的HTTP/1.0協議在每次響應後重新開始TCP串連的行為無法使用。
HTTP的一個優點是它正被廣泛的使用和接受。圖4表示了一個簡單的Java程式,它發送前面表示的請求並從響應中解析出結果字串。
下面則是一個簡單的C程式用CGI來讀取來自HTTP請求的字串並通過HTTP響應把它的逆序串返回。
#include <stdio.h>
int main(int argc, char **argv) {
char buf[4096];
int cb = read(0, buf, sizeof(buf));
buf[cb] = 0;
strrev(buf);
printf("200 OK\r\n");p>
printf("Content-Type: text/plain\r\n");
printf("Content-Length: %d\r\n", cb);
printf("\r\n");
printf(buf);
return 0;
圖5表示了一個更流行的版本,伺服器的實現是用Java servlet,以避免CGI的每個請求一個進程的開銷。
一般來說CGI是花費代價最小的寫HTTP伺服器端代碼的方法。實際上,每一個HTTP伺服器端產品都提供了一個更有效機制來讓你的代碼處理一個HTTP請求。IIS提供了ASP和ISAPI作為寫HTTP代碼的機制。Apache允許你用運行在Apache背景程式中的 C或Perl來寫模組。大多數應用伺服器軟體允許你寫Java servlet,COM組件,EJB Session beans或基於可攜帶對象適配器(POA)介面的CORBA servants。
XML 作為一個更好的網路資料表達方式(NDR)
HTTP是一個相當有用的RPC協議,它提供了IIOP或DCOM在組幀、串連管理以及序列化對象應用等方面大部分功能的支援。( 而且URLs與IORs和OBJREFs在功能上令人驚歎的接近)。HTTP所缺少的是用單一的標準格式來表達一個RPC調用中的參數。這則正是XML的用武之地。
象NDR和CDR,XML是一個與平台無關的中性的資料表達協議。XML允許資料被序列化成一個可以傳遞的形式,使得它容易地在任何平台上被解碼。XML有以下不同於NDR和CDR的特點:
有大量XML編碼和解碼軟體存在於每個編程環境和平台上
XML基於文本,相當容易用低技術水平的編程環境來處理
XML是特別靈活的格式,它容易用一致的方式來被擴充
為支援可擴充性,在XML中每一個元素和屬性有一個名域URI與它相聯絡,這個URI用xmlns屬性來指定。
考慮下面的XML文檔:
<reverse_string xmlns="urn:schemas-develop-com:StringProcs">
<string1>Hello, World</string1>
<comment xmlns='http://foo.com/documentation'>
This is a comment!!
</comment>
</reverse_string>
元素<reverse_string>和<string1>的名域URI是urn:schemas-develop-com:StringProcs。元素<comment>的名域URI是http://foo.com/documentation。第二個URI也是一個URL的事實是不重要的。在這兩種情況下,URI簡單地被用來消除元素<reverse_string>,<string1>,<comment>和任何碰巧有同樣標記名的其它元素間的歧義。
為了方便,XML允許名域URIs被映射為局部唯一的首碼。這意味著下面的XML文檔在語義上等同於上面的文檔:
<sp:reverse_string
xmlns:sp="urn:schemas-develop-com:StringProcs"
xmlns:doc='http://foo.com/documentation'
>
<sp:string1>Hello, World</sp:string1>
<doc:comment>
This is a comment!!
</doc:comment>
</sp:reverse_string>
後面的形式對作者來說更容易,尤其是如果有許多名域URIs在使用時。
XML也支援帶類型的資料表達。正在推出的XML Schema規範為描述XML資料類型標準化了一個詞彙集。下面是一個元素<reverse_string>的XML Schema的描述:
<schema
xmlns='http://www.w3.org/1999/XMLSchema'
targetNamespace='urn:schemas-develop-com:StringProcs'
>
<element name='reverse_string'>
<type>
<element name='string1' type='string' />
<any minOccurs='0' maxOccurs='*'/>
</type>
</element>
</schema>
這個XML Schema定義闡述了XML名域urn:schemas-develop-com:StringProcs包含了一個名為<reverse_string>的元素,這個元素包含了一個名為string1的子項目(類型為string),它被0個或更多沒有指定的元素所遵守。
XML Schema 規範還定義了一組內建的未經處理資料類型和建立一個XML文檔中元素的類型的機制。下面的XML文檔用XML Schema類型屬性來把元素和類型名聯絡在一起:
<customer
xmlns='http://customer.is.king.com'
xmlns:xsd='http://www.w3.org/1999/XMLSchema'
>
<name xsd:type='string'>Don Box</name>
<age xsd:type='float'>23.5</name>
</customer>
串連XML文檔案例到XML Schema描述的新的一個機制在本文寫作的時候正在標準化過程中。
HTTP + XML = SOAP
SOAP把XML的使用代碼化為請求和響應參數編碼模式,並用HTTP作傳輸。這似乎有點抽象。具體地講,一個SOAP方法可以簡單地看作遵循SOAP編碼規則的HTTP請求和響應。一個SOAP終端則可以看作一個基於HTTP的URL,它用來識別方法調用的目標。象CORBA/IIOP一樣,SOAP不需要具體的對象被綁定到一個給定的終端,而是由具體實現程式來決定怎樣把對象終端標識符映射到伺服器端的對象。
SOAP請求是一個HTTP POST請求。SOAP請求的content-type必須用text/xml。而且它必須包含一個請求-URI。伺服器怎樣解釋這個請求-URI是與實現相關的,但是許多實現中可能用它來映射到一個類或者一個對象。一個SOAP請求也必須用SOAPMethodName HTTP頭來指明將被調用的方法。簡單地講,SOAPMethodName頭是被URI指定範圍的應用相關的方法名,它是用#符作為分隔字元將方法名與URI分割開:
SOAPMethodName: urn:strings-com:IString#reverse
這個頭表明方法名是reverse,範圍URI是urn:strings-com:Istring。 在SOAP中,規定方法名範圍的名域URI在功能上等同於在DCOM 或 IIOP中規定方法名範圍的介面ID。
簡單的說,一個SOAP請求的HTTP體是一個XML文檔,它包含方法中[in]和[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頭的。這個頭只在請求訊息中需要,在響應訊息中並不需要。
圖6和圖7表明SOAP是怎樣與以前討論的ORPC協議相互對應的。讓許多SOAP新手困惑的是SOAP中沒有關於SOAP伺服器怎樣使用要求標頭來分發請求的要求;這被留為一個實現上的細節。一些SOAP伺服器將映射請求-URIs到類名,並指派調用到靜態方法或到在請求持續期記憶體活的類的執行個體。其它SOAP伺服器則將請求-URIs映射到始終存活的對象,經常是用查詢字串來編碼一個用來定位在伺服器處理序中的對象關鍵字。還有一些其它的SOAP伺服器用HTTP cookies來編碼一個對象關鍵字,這個關鍵字可被用來在每次方法請求中恢複對象的狀態。重要的是客戶對這些區別並不知道。客戶軟體只是簡單遵循HTTP和XML的規則來形成SOAP請求,讓伺服器自由以它認為最合適的方式來為請求服務。
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,DCOM,或IIOP代理)看上去象圖8中所表示的那樣。這裡,在發送請求訊息之前,參數被序列化成為一個請求對象。同樣被響應訊息接收到的響應對象被還原序列化為參數。一個類似的轉變同樣發生在調用的伺服器端。
當通過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:Body element elided for clarity —>
</soap:Envelope>
SOAP軟體遇到不能識別必須的頭元素情況時,必須拒絕這個訊息並出示一個錯誤。如果伺服器在一個SOAP請求中發現一個不能識別的必須的頭元素,它必須返回一個錯誤響應並且不發送任何調用到目標對象。如果用戶端在一個SOAP請求中發現一個不能識別出的必須的頭元素,它必須向調用者返回一個執行階段錯誤。(在COM情況下,這將映射為一個明顯的HRESULT)
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被稱著簡單存取元素,因為它們訪問對應於在W3C XML Schema規範 (見 http://www.w3.org/TR/XMLSchema-2) 的Part 2中定義的未經處理資料類型的值。這個規範指定了字串,數值,日期等資料類型的名字和表達方式以及使用一個新的模式定義中的<datatype>結構來定義新的原始類型的機制。
對引用簡單類型的存取元素,元素值被簡單地編碼為直接在存取元素下的字元資料,如上所示。對引用組合類別型的存取元素(就是那些自身用子存取元素來構造的存取元素),有兩個技術來對存取元素進行編碼。最簡單的方法是把被結構化的值直接嵌入在存取元素下。考慮下面的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' <!— false is the default —>
/>
</type>
在一個元素的Schema聲明中如果沒有nullable屬性,就意味著在一個XML文檔中的元素是不可為空的。Null存取元素的精確格式當前還在修訂中

相關文章

E-Commerce Solutions

Leverage the same tools powering the Alibaba Ecosystem

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。