<什么是WSDL語言>
WSDL(網絡服務描述語言,Web Services Description Language)是一門基於 XML 的語言,用於描述 Web Services 以及如何對它們進行訪問,參閱http://www.w3school.com.cn/wsdl/index.asp。
➤WSDL文檔可以分為兩部分,頂部分由抽象定義組成,而底部分由具體描述組成。
➣抽象部分
抽象部分以獨立於平台和語言的方式定義SOAP消息,它們並不包含任何隨機器或語言而變的元素。這就定義了一系列服務,截然不同的應用都可以實現。
➣具體部分
具體部分,如數據的序列化則歸入底部分,因為它包含具體的定義。在上述的文檔元 素中,<types>、<message>、<portType>屬於抽象定義 層,<binding>、<service>屬於具體定義層。所有的抽象可以是單獨存在於別的文件中,也可以從主文檔中導入。
Type :數據類型定義的包容器。對類型的描述可以用xsd來完成。
Message :定義通信中的數據。包括數據輸入和輸出。
Operation :對某項服務所能完成的一個動作進行的抽象定義。
Port :由一個綁定和一個網絡地址所定義的一個端點。
PortType :對一個或多個端口所支持的一組操作進行描述。
Binding :為一個給定的端口類型安排協議和數據格式。
Service :由一組相互關聯的端口所構成的一個聚合。
<WDSL實例>
通過http://localhost:9000/helloWorld?wsdl可以查看到XML文件,這就是WSDL(WebService Definition Language),將該wsdl文件通過瀏覽器“將頁面另存為”保存為helloWorld.wsdl。完整的 WSDL 語法見http://www.w3school.com.cn/wsdl/wsdl_syntax.asp
<?xml version='1.0' encoding='UTF-8'?>
<wsdl:definitions name="HelloWorld"
targetNamespace="http://server.cxf.webservice.web.apps.lucl.com/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns="http://server.cxf.webservice.web.apps.lucl.com/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:ns1="http://schemas.xmlsoap.org/soap/http"
>
<!-- 定義數據類型 -->
<wsdl:types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://server.cxf.webservice.web.apps.lucl.com/"
elementFormDefault="unqualified"
targetNamespace="http://server.cxf.webservice.web.apps.lucl.com/"
version="1.0">
<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:schema>
</wsdl:types>
<!-- 定義消息分組 -->
<wsdl:message name="sayHiResponse">
<wsdl:part element="tns:sayHiResponse" name="parameters" />
</wsdl:message>
<wsdl:message name="sayHi">
<wsdl:part element="tns:sayHi" name="parameters" />
</wsdl:message>
<!-- 定義port type -->
<wsdl:portType name="HelloWorldPortType">
<wsdl:operation name="sayHi">
<wsdl:input message="tns:sayHi" name="sayHi" />
<wsdl:output message="tns:sayHiResponse" name="sayHiResponse" />
<!-- 指定當 Web 服務設法響應客戶機的請求時所發生的任何消息級異常 -->
<!--
<wsdl:fault name="" message=""></wsdl:fault>
-->
</wsdl:operation>
</wsdl:portType>
<!-- binding操作到特定協議,即關聯portType到協議,這里為SOAP -->
<!-- W3C 推薦了三個 Web 服務的綁定:
HTTP 上的 SOAP(SOAP over HTTP)
HTTP GET/POST
SOAP/MIME
-->
<wsdl:binding name="HelloWorldSoapBinding" type="tns:HelloWorldPortType">
<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>
<!-- 應用portType處的fault -->
<!--
<wsdl:fault name="">
<soap:fault name="" use="literal"/>
</wsdl:fault>
-->
</wsdl:operation>
</wsdl:binding>
<!--
描述binding的連接信息,根據綁定所實現的 portType 來處理請求。
對於 HTTP 上的 SOAP,這就是指向那個進程的 URL。
-->
<wsdl:service name="HelloWorld">
<wsdl:port binding="tns:HelloWorldSoapBinding" name="HelloWorldPort">
<soap:address location="http://localhost:9000/helloWorld" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
<wsdl:definition>
WSDL的根元素,主要屬性為name和targetNamespace兩個。
name為公開的Web服務接口,默認為實現類+Service,可通過WebService的serviceNmae指定;
targetNamespace指定目標名稱空間,屬性值同樣被后面的xmlns:tns屬性作為值,默認是使用接口實現類的包名的反序(http://server.cxf.webservice.web.apps.lucl.com/)。
<wsdl:types>
xs:schema是定義xml的屬性信息,如命名空空間。
通過<xs:element... 和<xs:complexType...對元素進行說明,wsdl2java會根據element生成java類,而根據complexType創建類的屬性。<xs:element...>
name為定義的方法sayHi以及元素sayHiResponse,
type屬性是tns:+name屬性對應的方法名。
sayHi是對方法的封裝,sayHiResponse是對返回值的封裝。
說明:可以使用WebMethod的operationName屬性來自定義名稱。
<xs:complexType...>
這個元素通過name屬性關聯到<xs:element...,它為element元素指定封裝的具體內容,
通過子元素<xs:sequence...指定(參數名和參數類型)。
@WebService(name="HelloWorldPortType")
public interface HelloWorld {
@WebMethod(operationName="cusSayHi")
@WebResult(name="sayHiToUser") String sayHi(@WebParam(name="text") String text);
}
<wsdl:message>
這個元素將輸入參數(方法參數)和響應結果(方法返回值)、受檢查的異常信息包裝為消息。
<wsdl:portType>
指定Web服務的端口類型(Web服務會被發布為EndPoint端點服務),它的name屬性默認為接口名稱(可以使用@WebService 注解的name 屬性指定值,默認為實現類+Port)。
子元素<wsdl:operation …指定該端點服務包含了那些操作( 方法),input/output指定操作的輸入輸出(通過屬性message 綁定到前面聲明過的消息)。
<wsdl:operation… 的子元素>
<wsdl:input message="tns:sayHi" name="sayHi" />
<wsdl:output message="tns:sayHiResponse" name="sayHiResponse" />
<wsdl:binding>
將前面的端點服務綁定到SOAP協議,其中<soap:xxx... 的style、use分別可以使用SOAPBinding注解的style、use屬性指定值。<wsdl:operation...>
指定公開的操作(方法)
<wsdl:service>
name 屬性指定服務名稱。
與根元素name值相同,可通過WebService的serviceName屬性指定,默認為實現類+Service。
子元素<wsdl:port… 的name 屬性指定port 名稱,可通過WebService的屬性portName指定,默認為實現類+Port。
子元素<soap:address … 的location 屬性指定Web 服務的地址。
<深入理解WSDL>
曾經有人說soap並不真需要什么接口描述語言。如果soap是交流純內容的標准,那就需要一種語言來描述內容。soap消息確實帶有某些 類型信息 ,因此soap允許動態的決定類型。但不知道一個函數的函數名,參數的個數和各自類型,怎么可能去調用這個函數呢?沒有wsdl,可以從必備文檔中確定調用語法,或者檢查消息。即便何種方法,都必須有人參與,這個過程可能會出錯。而使用了wsdl,就可以通過這種跨平台和跨語言的方法使web service代理的產生自動化。
每個wsdl都定義了一項服務(service),而這項服務被定義為一組“端口(port)”。你可以把wsdl中的端口想象為URL地址,而不是TCP/IP中的數據通道。一個端口定義了一個”服務”項目的提供地點。一項服務可以有多個提供地點,但只能對一組事先安排好的“信息(message)”作出響應。“信息”是對通信數據的描述,每條信息由一組數據組成,這些數據必須定義為收發雙方都知道的某種“類型(type)”。如果沒有遇到其他方法更能說明問題的情況,則”類型”必須用XSD來定義。端口和信息結合在一起代表了一組“操作(operation)”,並定義了這個端口的“端口類型(portType)”。把一種協議和一種數據格式關聯在一起就定義了一種可重復使用的“綁定(binding)”。把一個網絡地址和一個綁定關聯在一起就定義出了一個端口,而一組端口將定義出一項服務。綜上所述,wsdl文檔使用下面這些元素來定義一項網絡服務:
Type :數據類型定義的包容器。對類型的描述可以用xsd來完成。
Message :定義通信中的數據。包括數據輸入和輸出。
Operation :對某項服務所能完成的一個動作進行的抽象定義。
Port :由一個綁定和一個網絡地址所定義的一個端點。
PortType :對一個或多個端口所支持的一組操作進行描述。
Binding :為一個給定的端口類型安排協議和數據格式。
Service :由一組相互關聯的端口所構成的一個聚合。
在開始討論這些術語之前,需要對wsdl技術標准所使用的命名空間前綴有一個了解。大家應該習慣使用這些前綴。wsdl技術標准所使用的名字空間前綴見下表。

任何web服務文檔都是由一系列定義語句組成的。wsdl在她的xsd文檔里定義了以下幾種元素:
➣definitions
所有xml文檔需要有一個頂級文檔作為樹結構的 根 。而wsdl技術標准選定的就是這個。
➣documentation
這個元素可以包含任意文本和元素。只要你就覺得有必要在文檔里的某個位置加上一些額外的資料幫助其他人閱讀和理解,就可以向這個元素注射一個注釋文檔進來。
➣message
對服務過程中鎖發送和接收的數據進行的抽象定義。它可以由多個邏輯部分組成,每個部分必須與某個類型相關聯。
➣portType
對給定端口上的可用操作集合進行的定義。
➣operation
給操作起個名字,並列出預期的輸入和輸出情況。每個元素還可以包含一個對該operation可能返回的出錯數據進行描述的fault子元素。
➣input
對給定操作用作輸入參數的元素進行描述。它還可以把input鏈接到一個指定的信息。
➣output
對給定操作作為輸出參數返回的元素進行描述。它還可以把output鏈接到一個指定的信息。
➣fault
對可能返回的出錯數據進行定義。
➣binding
為給定的portType所定義的操作和信息指定協議和數據格式。
➣service
用來把相關的端口組織在一起。
➣port
為一個給定的綁定分配地址。它通常以URN標識符的形式出現。一個典型的wsdl文檔看起來使這個樣子(其中的*,?,+就是普通的通配符含義,代表該元素出現的限制):


➣下圖描述了他們之間的關系:

可以參考wsdl實例:
http://www.blogjava.net/charles/archive/2008/12/15/246368.html
注意:上面這段代碼中的“ import ”元素。這個元素允許把一個命名空間和一個文檔存儲位置關聯在一起。可以用來建立服務定義,還可以通過它重復使用其他文檔里的部分或全部定義內容。下面是示例對比:
➤ 不帶import元素的wsdl文檔

我們看到上面的和之間的類型定義無非是一些嵌入的xml schema,所以把他們單獨提取出來作為一個文件時很簡單的。
➤帶有import的wsdl
➣下面先提取出http://example.com/stockquote/stockquote.xsd的內容


下面是完整的http://example.com/stockquote/stockquoteservice.wsdl的內容。


➤擴展元素和綁定
wsdl用綁定這個術語表示把協議和數據格式信息與一個信息,一個端口類型或者一個操作等關聯在一起的動作。綁定中的某些元素表示某種特殊技術,wsdl技術標准稱這類元素為擴展元素。這類元素一般用來對某種給定協議或者信息格式所特有的綁定信息進行定義。比如說,http擴展元素可以用來指定某個web站點里某個特定的web主頁;而smtp擴展元素指定一個電子郵件地址。wsdl技術標准對擴展元素在wsdl定義里的出現位置做了規定,bool屬性wsdl:required指明某個元素是否是必不可少的,它的缺省值是false(0)。
➤對類型信息進行編碼
wsdl把xsd作為它首選的類型定義系統。這使你能夠對基於二進制數據的協議進行綁定。
➣ 信息
信息(message)定義里包含一個或多個操作(operation)部分。message定義通過一個信息類型定義屬性把每一個操作部分和一種類型關聯在一起。因此,可以根據具體情況使用多個屬性。為了配合xsd的使用,wsdl定義了名為 ”element”和”type” 的兩個屬性。wsdl中的element屬性通過一個假名(Qname)對應一個xsd中的element; type屬性通過一個Qname對應着一個xsd中的simpleType或complexType。舉個例子,假設在大綱里有下面的定義:

完整的各個部分的實例解釋還可以參考: http://blog.csdn.net/simbi/article/details/6231151
➤端口類型
wsdl中的端口(port)與基於套接字進行網絡程序設計時說的端口含義是不同的。在wsdl世界里,一個端口類型(portType)實例指的是一組operation構成的特定組合。這個portType可能會被綁定到某個特定的TCP/IP端口,但這只不過是一種巧合而已。
portType定義的是一組operation,是一組抽象的操作。每個operation元素定義了調用PortType中所有方法的語法,每個operation最多可以由三個元素組成:wsdl:input,wsdl:output和wsdl:fault。有了這幾個元素,就可以對operation和服務斷點所支持的基本傳輸方式(單向,請求/響應等)作出定義。
某些基本傳輸方式可以帶一個參數順序(parameterOrder)屬性。這個屬性的作用是對綁定中使用的參數是否需要按一定順序排列進行規定。operation定義對“是否需要在RPC類型的綁定中按一定順序排列參數”是不做規定的。
如基於請求/響應的基本定義格式:

綁定(binding)語句的作用是為一個給定portType所定義的operation和message定義格式和協議細節,這個元素只能指定一種協議,不能出現任何地址信息。一個portType可以有一個或多個綁定定義。 屬性可以包含input,output,fault元素,他們都對應於portType中的相同元素(通過name屬性引用) 。他們有下面的語法:

<ignore_js_op style="color:rgb(0, 0, 0); font-family:微軟雅黑, Helvetica, 'Hiragino Sans GB', 微軟雅黑, 'Microsoft YaHei UI', SimSun, SimHei, arial, sans-serif; font-size:1rem; font-style:normal; font-weight:normal; text-align:start; text-indent:0px;">

在這種情況下,擴展元素將為port提供地址信息。一個port只能給出一個地址,而且不能用來給出地址信息以外的其他信息。service和port兩個元素的名字屬性在整個wsdl文檔的范圍內必須唯一。一個service里面的各個port必須尊守以下規則:
➣各個port之間不得進行通信,也就是不能有依賴關系。
➣一個service中允許存在其綁定映射到某個portType或映射到其他不同的地址上的port,而且這樣的port可以有多個。
➣可以通過檢查某項service的服務端口來確定該項service所提供的portType。用戶可以利用這方面信息查明給定機器是否支持完成某項給定任務所需要的全部operation。
➤soap綁定
wsdl通過一組擴展元素來定義如何使用soap接收和發送請求。這些元素提供以下幾方面信息:
➣使用soap 1.1協議的port都有哪些
➣為一個soap端口指定一個地址。
➣為http信息頭中的SOAPAction指定URI取值,這個值的作用是指明數據傳輸工作是否將在http協議上完成。
➣列出soap信息頭的定義情況,這些Header元素可能出現在SOAP的封套中。
➣它提供一種用xsd對soap根進行定義的手段。
soap綁定用到的擴展元素的語法定義如下:


<ignore_js_op style="color:rgb(0, 0, 0); font-family:微軟雅黑, Helvetica, 'Hiragino Sans GB', 微軟雅黑, 'Microsoft YaHei UI', SimSun, SimHei, arial, sans-serif; font-size:1rem; font-style:normal; font-weight:normal; text-align:start; text-indent:0px;">


➤soap:binding元素
這個元素指明整個綁定被綁定到soap上,包括soap的封套,信息頭,信息體和出錯信息等元素。 為了把一個binding綁定到soap格式,必須使用soap:binding元素。 這樣,wsdl就跟soap關聯上了。
soap:binding有兩個可選的屬性。transport屬性指明這個binding對應哪一個soap傳輸機制。用來表明“這是一個符合soap技術標准的http綁定”的uri值是http://schemas.xmlsoap.org/soap/http。還可以使用其他uri來表明其他傳輸機制,比如smtp或純tcp等。
➤soap:operation元素
這個元素提供了關於整個operation集的信息。
➣它有一個soapAction屬性,作用是對預期的http SOAPAction信息頭進行定義。在發出請求時不要修改這個值。只有在http上使用soap時才需要這個屬性。
➣還有一個style屬性,它的作用是給出這個operation的類別:面向RPC還是面向文檔。如果信息里包含有調用參數和返回值,就要使用”rpc”作為屬性值;如果信息里包含的是文檔,就要使用”document”作為屬性的值。
➤soap:body元素
soap:body元素對信息的各組成部分在soap信息體元素里的排列順序進行定義。信息的組成部分既可以用直觀的大綱來定義,也可以用抽象的類型定義來定義。
這個元素有一個可選的parts屬性,作用是告訴wsdl文檔的閱讀者(程序)信息的哪些組成部分將出現在信息的soap信息里。如果省略這個屬性,就表示信息的全體組成部分都將出現在soap信息體里。
soap:body元素必須永遠包含一個use屬性,這個屬性的合法取值有兩個,即”literal”和”encoded”。當設置為”encoded”時,信息中的各個部分將通過type屬性指向某個抽象類型;當使用由encodingStyle指定的編碼方案對信息進行編碼時,那些抽象類型將生成一條確切的信息,在part定義里給出的name,type和namespace屬性都成為編碼方案的輸入信息。namespace屬性只有在抽象類型的type屬性沒有明確地對名字空間做出定義時才起作用。“由讀取信息的那一方設置正確”只有在編碼方案允許抽象類型的信息格式有所變化時才對信息起作用。soap技術標准中給出的編碼方案也確實允許信息的格式有細微的變化。
如果想讓信息“由寫出信息的那一方設置正確”,就必須把use屬性設置為”literal”。這意味着信息的每一個part都有直觀的大綱。這種情況下,encodingStyle屬性可以用來指明將使用哪一種編碼方案來推導出實際完成編碼操作的編碼方案。
encodingStyle屬性由一系列URI組成,他們彼此之間用一個空格隔開。
➤soap:fault元素
soap:fault元素用來對將出現在soap的fault元素里的信息元素進行定義。它的name,use,encodingStyle和namespace屬性與soap:body元素的同名屬性作用相同。這個元素只能有一個part。
➤soap:header元素
soap:header元素提供了對將出現在soap信息頭元素里的信息部分進行定義的能力。不必把可能出現的每一條信息標題都列出來,因為wsdl文檔里的中間環節和其他定義可能會把一些你不知道的信息標題加到最終的信息結果里去。信息標題的類型是通過element屬性設置的。用於這個屬性的大綱不得包括對soap信息頭的actor和mustUnderstand屬性的定義。
這個元素有一個可選的fault屬性。
➤soap:address元素
soap:address元素把一個URI和一個端口關聯在一起。把一個端口綁定到soap上的時候,給出的必須是一個地址,其他任何東西都是錯誤的。
➤ GET/POST綁定和MIME綁定
為了描述web瀏覽器和web站點之間的交互操作,wsdl技術標准對http的get和post動作的綁定進行了規范。
wsdl還定義了一種通過MIME格式把抽象類型綁定到最終信息上的辦法。
➣首先是了解消息頭header和ProbeMatches中的內容,非常重要,可以參考這里http://www.w3.org/Submission/ws-addressing/ 最好詳細的學習一下,里面的內容非常重要。
➣其次需要理解的是,其實當你看完ws-addressing后你會發現,骨架代碼中的結構體和SOAP消息中的內容是一一對應的,例如:
結構體osap->header對應SOAP消息的<SOAP-ENV:Header></SOAP-ENV:Header>中的內容,包含在header里的內容當然會包含在SOAP的header內。例如:
結構體soap->header->wsa__RelatesTo對應的是<wsa:RelatesTo></wsa:RelatesTo>。
➣最后需要理解的是,在代碼中的"__"雙下划線一般對應xml中的命名空間的":",下划線前是命名空間,后是具體內容。
➣最后的最后是要詳細的閱讀ONVIF Core Specification
下圖為響應OnvifTestTool的Probe命令的SOAP消息
結合上圖再分析代碼就親切多了。在ONVIF Core Specification的7.3.2.2 Scopes 一節描述了onvif需要的Scopes,這個是需要在程序里填充,具體填充什么,文檔里說的很明確:
注意點是在太多,隨便漏掉一個都可能會導致搜不到設備,下圖是非常重要的一個:
SOAP1.1和SOAP1.2所使用的SOAP-ENV是不同的,ONVIF使用的是SOAP1.1,如果soapcpp2產生的nsmap文件中的SOAP-ENV是SOAP1.2版本的話,那么OnvifTestTool是不會識別設備發出的SOAP消息的。
<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">