1. 程式人生 > >Web Service筆記(三):wsdl 與 soap協議詳解

Web Service筆記(三):wsdl 與 soap協議詳解

一、WSDL語言:(web service definition language - web service定義語言)

(一)簡介:

2、wsdl 文件描述了 ws 主要的3個方面:

1)WHATA:該 ws 包含”什麼“操作,即有幾個方法。

2)HOW:該 ws 的操作應該”怎樣“呼叫?

3)WHERE:該 ws 的服務地址。

1)實現類的 wsdl 地址為:http://localhost:8080/helloWorld?wsdl 。內容如下:

This XML file does not appear to have any style information associated with it. The document tree is shown below.
<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://impl.ws.com/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns2="http://schemas.xmlsoap.org/soap/http" xmlns:ns1="http://ws.com/" name="HelloSAM" targetNamespace="http://impl.ws.com/">
<wsdl:import location="http://localhost:8080/helloWorld?wsdl=HelloWorld.wsdl" namespace="http://ws.com/"></wsdl:import>
<wsdl:binding name="HelloSAMSoapBinding" type="ns1:HelloWorld">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="sayHi">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="sayHi">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="sayHiResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="getCatsByUser">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="getCatsByUser">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="getCatsByUserResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="HelloSAM">
<wsdl:port binding="tns:HelloSAMSoapBinding" name="HelloWorldImplPort">
<soap:address location="http://localhost:8080/helloWorld"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

 2)介面的 wsdl 地址為:http://localhost:8080/helloWorld?wsdl=HelloWorld.wsdl。內容如下: 
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:ns1="http://ws.com/" name="HelloWorld" targetNamespace="http://ws.com/">
<wsdl:types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://ws.com/" elementFormDefault="unqualified" targetNamespace="http://ws.com/" version="1.0">
<xs:element name="getCatsByUser" type="tns:getCatsByUser"/>
<xs:element name="getCatsByUserResponse" type="tns:getCatsByUserResponse"/>
<xs:element name="sayHi" type="tns:sayHi"/>
<xs:element name="sayHiResponse" type="tns:sayHiResponse"/>
<xs:complexType name="sayHi">
<xs:sequence>
<xs:element minOccurs="0" name="arg0" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="sayHiResponse">
<xs:sequence>
<xs:element minOccurs="0" name="return" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="getCatsByUser">
<xs:sequence>
<xs:element minOccurs="0" name="arg0" type="tns:user"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="user">
<xs:sequence>
<xs:element minOccurs="0" name="address" type="xs:string"/>
<xs:element minOccurs="0" name="id" type="xs:int"/>
<xs:element minOccurs="0" name="name" type="xs:string"/>
<xs:element minOccurs="0" name="pass" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="getCatsByUserResponse">
<xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0" name="return" type="tns:cat"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="cat">
<xs:sequence>
<xs:element minOccurs="0" name="color" type="xs:string"/>
<xs:element minOccurs="0" name="id" type="xs:int"/>
<xs:element minOccurs="0" name="name" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
</wsdl:types>
<wsdl:message name="getCatsByUserResponse">
<wsdl:part element="ns1:getCatsByUserResponse" name="parameters"></wsdl:part>
</wsdl:message>
<wsdl:message name="sayHiResponse">
<wsdl:part element="ns1:sayHiResponse" name="parameters"></wsdl:part>
</wsdl:message>
<wsdl:message name="getCatsByUser">
<wsdl:part element="ns1:getCatsByUser" name="parameters"></wsdl:part>
</wsdl:message>
<wsdl:message name="sayHi">
<wsdl:part element="ns1:sayHi" name="parameters"></wsdl:part>
</wsdl:message>
<wsdl:portType name="HelloWorld">
<wsdl:operation name="sayHi">
<wsdl:input message="ns1:sayHi" name="sayHi"></wsdl:input>
<wsdl:output message="ns1:sayHiResponse" name="sayHiResponse"></wsdl:output>
</wsdl:operation>
<wsdl:operation name="getCatsByUser">
<wsdl:input message="ns1:getCatsByUser" name="getCatsByUser"></wsdl:input>
<wsdl:output message="ns1:getCatsByUserResponse" name="getCatsByUserResponse"></wsdl:output>
</wsdl:operation>
</wsdl:portType>
</wsdl:definitions>


(二)語法詳解:

1、根元素:definitions 

1)targetNamespace :相當於java的 package 。

如 服務端實現類與 wsdl 檔案的 targetNamespace 為 如下,應該是一致的。

package com.ws.impl;//實現類的包
targetNamespace="http://impl.ws.com/"//wsdl的targetNamespace

2)xmlns :遵守的名稱空間的schema 檔案,相當於java的 import 。即本文件引進了這個schema規範,需要遵守它的語法。可以有別名,用來區分引進不同的schema規範。

xmlns:xsd="http://www.w3.org/2001/XMLSchema"//xsd為別名


3)import :匯入的介面檔案。通過 namespase的路徑也可以看出。
<wsdl:import location="http://localhost:8080/helloWorld?wsdl=HelloWorld.wsdl" namespace="http://ws.com/"></wsdl:import>


2、WS介面的wsdl語法。wsdl 顯示的服務端內容都在此

1)types元素 :該元素內容是標準的xml Schema文件

<wsdl:types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://ws.com/" elementFormDefault="unqualified" targetNamespace="http://ws.com/" version="1.0">
<xs:element name="getCatsByUser" type="tns:getCatsByUser"/>
<xs:element name="getCatsByUserResponse" type="tns:getCatsByUserResponse"/>
<xs:element name="sayHi" type="tns:sayHi"/>
<xs:element name="sayHiResponse" type="tns:sayHiResponse"/>
<xs:complexType name="sayHi">
<xs:sequence>
<xs:element minOccurs="0" name="arg0" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="sayHiResponse">
<xs:sequence>
<xs:element minOccurs="0" name="return" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="getCatsByUser">
<xs:sequence>
<xs:element minOccurs="0" name="arg0" type="tns:user"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="user">
<xs:sequence>
<xs:element minOccurs="0" name="address" type="xs:string"/>
<xs:element minOccurs="0" name="id" type="xs:int"/>
<xs:element minOccurs="0" name="name" type="xs:string"/>
<xs:element minOccurs="0" name="pass" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="getCatsByUserResponse">
<xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0" name="return" type="tns:cat"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="cat">
<xs:sequence>
<xs:element minOccurs="0" name="color" type="xs:string"/>
<xs:element minOccurs="0" name="id" type="xs:int"/>
<xs:element minOccurs="0" name="name" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
</wsdl:types>

   
2)message元素:有2N個message元素,需要有傳入傳出訊息。如服務端方法public String sayHi(String name){}方法:
<wsdl:message name="getCatsByUserResponse">
<wsdl:part element="ns1:getCatsByUserResponse" name="parameters"></wsdl:part>
</wsdl:message>
<wsdl:message name="sayHiResponse">
<wsdl:part element="ns1:sayHiResponse" name="parameters"></wsdl:part>
</wsdl:message>
<wsdl:message name="getCatsByUser">
<wsdl:part element="ns1:getCatsByUser" name="parameters"></wsdl:part>
</wsdl:message>
<span style="color:#ff0000;"><wsdl:message name="sayHi">
<wsdl:part element="ns1:sayHi" name="parameters"></wsdl:part>
</wsdl:message></span>

①、message 元素中有name等於 sayHi 的屬性。可以理解為 一個 sayHi 的訊息。

②、子元素 part中有element屬性為 sayHi 。理解為 sayHi的訊息需要一個 sayHi 的元素(element)。這個元素在上面的 type 中會有定義。


3)portType元素: N個operation子元素,每個operation代表一個ws操作(即方法),包含請求與回覆2次。

<wsdl:portType name="HelloWorld">
<wsdl:operation name="sayHi">
<wsdl:input message="ns1:sayHi" name="sayHi"></wsdl:input>
<wsdl:output message="ns1:sayHiResponse" name="sayHiResponse"></wsdl:output>
</wsdl:operation>
<wsdl:operation name="getCatsByUser">
<wsdl:input message="ns1:getCatsByUser" name="getCatsByUser"></wsdl:input>
<wsdl:output message="ns1:getCatsByUserResponse" name="getCatsByUserResponse"></wsdl:output>
</wsdl:operation>
</wsdl:portType>

①、服務端有兩個操作的方法 sayHi() 和 getCatsByUser() ,在 portType 元素中就會有 2個 operation ,分別為 sayHi 和 getCatsByUser 。

②、每個 operation元素中有input 和 output 子元素,定義了這次操作需要的 message元素資訊,在上面定義。

4)顯而易見,這邊有一定的依賴關係存在:一個 WS 服務端實現類,定義了多個方法(operation元素),方法的呼叫需要明確傳入傳出引數,被定義在message元素中,稱為訊息。每個定義的訊息有依賴於element元素,定義在 types 中,是一份標準的 schema。

5)整個介面的 wsdl 由於上述依賴的關係,可以倒過來分析:如方法public List<Cat> getCatsByUser(User user) {},其實其本質規定了呼叫該方法傳入引數的型別與規範。

①、一個介面,定義方法為 getCatsByUser。wsdl 定義為:一個 portType 為 HelloWorld 的介面,有名為 getCatsByUser 的 operation,即有這個方法可呼叫。這個方法需要name為 getCatsByUser  和 getCatsByUserResponse 的 message 元素(input 和 output ),代表了這個方法的傳入與傳出訊息(即方法的引數)。

②、方法的傳入傳出引數。message 元素中規定這個 getCatsByUser  的傳入訊息實際內容在名為 getCatsByUser 的element元素中,element元素定義在types元素中。

③、element元素中規定 getCatsByUser  遵守名字為getCatsByUser  的schema語法定義,使用type關鍵字實現sechema語法的複用。

④、名字為 getCatsByUser  的 schema定義為:complex為複雜型別,0-1個,sequesce指要有順序,type指向名字為 user 的schema定義。

⑤、名字為 user 的 schema定義為: 有順利的四個元素,分別為address、id、name、pass,出現0-1次。

4、本質:一個ws,其實並不是方法的呼叫,而是傳送soap訊息(即xml文件片段)

1)客戶端把呼叫的方法引數,轉換生成xml文件片段(soap訊息)——必須符合wsdl規定的格式

2)客戶端通過網路,把xml文件片段傳給伺服器。

3)伺服器接收到xml文件片段。

4)伺服器解析xml文件片段,提取其中的資料。返回xml文件。

如getCatsByUser(User user) 方法,傳入傳出的是這樣的xml片段:

<!--傳入的引數-->
<getCatsByUser>
	<arg0>
		<address></address>
		<id></id>
		<name></name>
		<pass></pass>
	</arg0>
</getCatsByUser>

<!--返回的引數-->
<getCatsByUserResponse>
	<return>
		<color></color>
		<id></id>
		<name></name>
	</return>
	<return>
		<color></color>
		<id></id>
		<name></name>
	</return>
</getCatsByUserResponse>

3、WS實現類:

1)、binding:指定ws的函式風格,並詳細的定義了介面中的操作即 operation 。函式風格,現在一般為 document 文件風格。

<wsdl:binding name="HelloSAMSoapBinding" type="ns1:HelloWorld">
<soap:binding <span style="color:#ff0000;">style="document" </span>transport="http://schemas.xmlsoap.org/soap/http"/>
<<span style="color:#ff0000;">wsdl:operation </span>name="sayHi">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="sayHi">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="sayHiResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="getCatsByUser">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="getCatsByUser">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="getCatsByUserResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>


2)、service:name-定義了ws的名稱,即程式碼中使用 serviceName 指定的名稱,port【address】-定義ws繫結的地址。

<wsdl:service name="HelloSAM">
<wsdl:port binding="tns:HelloSAMSoapBinding" name="HelloWorldImplPort">
<soap:address location="http://localhost:8080/helloWorld"/>
</wsdl:port>
</wsdl:service>


二、SOAP語言(簡單訪問物件協議)

(一)通過使用CXF的日誌攔截器,我們可以在控制檯輸出攔截器的soap訊息。服務端攔截如下:

1、sayHi()操作的soap訊息:

傳入:

Headers: 
		{Accept=[text/xml, multipart/related, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2], 
		connection=[keep-alive], 
		Content-Length=[186], 
		content-type=[text/xml; charset=UTF-8], 
		Host=[localhost:8080], 
		SOAPAction=[""], 
		User-Agent=[Java/1.6.0_10-rc2]}


Payload:				
		<?xml version="1.0" ?>
		<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
			<S:Body>
				<ns2:sayHi xmlns:ns2="http://ws.com/">
					<arg0>SAM-SHO</arg0>
				</ns2:sayHi>
			</S:Body>
		</S:Envelope>

		
傳出:
	Headers: {}
	Payload: 
		<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
			<soap:Body>
				<ns2:sayHiResponse xmlns:ns2="http://ws.com/">
					<return>SAM-SHO,您好!您現在訪問的是簡單的WS服務端,時間為:14-11-20 下午1:28</return>
				</ns2:sayHiResponse>
			</soap:Body>
		</soap:Envelope>

2、getCatsByUser()操作的soap訊息:與上面wsdl分析的訊息應該是一致的。
傳入:

<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
	<S:Body>
		<ns2:getCatsByUser xmlns:ns2="http://ws.com/">
			<arg0>
				<address>soochow</address>
				<id>1</id>
				<name>Sam-Sho</name>
				<pass>1234</pass>
			</arg0>
		</ns2:getCatsByUser>
	</S:Body>
</S:Envelope>

傳出:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
	<soap:Body>
		<ns2:getCatsByUserResponse xmlns:ns2="http://ws.com/">
			<return>
				<color>黃色</color>
				<id>1</id>
				<name>加菲貓</name>
			</return>
			<return>
				<color>藍色</color>
				<id>2</id>
				<name>藍胖子</name>
			</return>
			<return>
				<color>粉色</color>
				<id>3</id>
				<name>hello kitty</name>
			</return>
			<return>
				<color>黑白色</color>
				<id>4</id>
				<name>熊貓</name>
			</return>
		</ns2:getCatsByUserResponse>
	</soap:Body>
</soap:Envelope>


(二)soap語法

	<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
			<soap:Header>
				<!--可能會有Header元素-->
			</soap:Header>
			<soap:Body>
				<ns2:sayHiResponse xmlns:ns2="http://ws.com/">
					<return>SAM-SHO,您好!您現在訪問的是簡單的WS服務端,時間為:14-11-20 下午1:28</return>
				</ns2:sayHiResponse>
			</soap:Body>
		</soap:Envelope>


1、根元素:Envelope。

2、Header元素::不是強制出現,由程式設計師控制,主要用於攜帶一些額外的資訊,比如使用者名稱、密碼

3、Body:

1)呼叫正確,body元素內容應該遵守WSDL要求的格式。

	<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
		<soap:Body>
			<ns2:sayHiResponse xmlns:ns2="http://ws.com/">
				<return>SAM,您好現在的時間是:Thu Jan 09 10:19:57 CST 2014</return>
			</ns2:sayHiResponse>
		</soap:Body>
	</soap:Envelope>

2)呼叫錯誤,body元素內容顯示Faulty元素。

<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>
No binding operation info while invoking unknown method with params unknown.
</faultstring>
</soap:Fault>
</soap:Body>

4、return:返回資訊。

三、UDDI(可以省略)