wcf——WSDL
使用WCF發布的Web服務可以被各種技術平臺遠程調用,關鍵就是WCF發布了符合業界標準的WSDL(Web Service Description Language),各種技術平時使用各自的工具將這種WSDL解釋成自身所能接受的編程對象,讓后對其進行服務調用。本系列文章旨在學習控制WCF生成WSDL。
為了實驗之用,我首先寫了一個很簡單的WCF應用程序,同樣由契約類庫、服務實現類庫、服務宿主程序、客戶端程序組成。(項目文件在下載區,文章中只貼出關鍵代碼)
以下是服務契約的定義:
代碼 namespace WCF_Study3.Contracts{
[ServiceContract]
public interface IContract
{
[OperationContract]
void NoArgsOperation();
[OperationContract]
void OneArgOperation(int arg1);
[OperationContract]
void TwoArgOperation(int arg1, string arg2);
[OperationContract]
void MultiArgsOperation(params double[] args);
[OperationContract]
object OperationWithReturnArg(object argObj1);
}
}
契約中定義的方法簽名都是一些比較典型的例子,不過在這里沒有涉及以自定義類型為參數或返回值的方法簽名,因為我還不想馬上就要進行數據契約的內容。注意到我在以上的例子中沒有添加任何附加的WSDL元數據(例如Name、Namespace等等),這些元數據在后面加入,我覺得這樣可以更加清晰地看出這些元數據在WCF中被加入后,如何反映到WSDL。我對這個服務契約的實現非常簡單(幾乎跟沒實現一樣。。),因此就不發出來了,同樣,部署服務宿主的代碼暫時與本文主題無關,因此也不發了,如果想要知道怎么部署服務細節,請參考我的其他文章。以下就是這個服務契約生成的部分WSDL(順帶一句,本人愚鈍,想了快10分鐘才知道怎么可以看到WSDL的內容,因此在這里分享一下,就是在瀏覽器中輸入發布元數據的URL。。):
代碼 <wsdl:portType name="IContract"><wsdl:operation name="NoArgsOperation">
<wsdl:input wsaw:Action="http://tempuri.org/IContract/NoArgsOperation" message="tns:IContract_NoArgsOperation_InputMessage"/>
<wsdl:output wsaw:Action="http://tempuri.org/IContract/NoArgsOperationResponse" message="tns:IContract_NoArgsOperation_OutputMessage"/>
</wsdl:operation>
<wsdl:operation name="OneArgOperation">
<wsdl:input wsaw:Action="http://tempuri.org/IContract/OneArgOperation" message="tns:IContract_OneArgOperation_InputMessage"/>
<wsdl:output wsaw:Action="http://tempuri.org/IContract/OneArgOperationResponse" message="tns:IContract_OneArgOperation_OutputMessage"/>
</wsdl:operation>
<wsdl:operation name="TwoArgOperation">
<wsdl:input wsaw:Action="http://tempuri.org/IContract/TwoArgOperation" message="tns:IContract_TwoArgOperation_InputMessage"/>
<wsdl:output wsaw:Action="http://tempuri.org/IContract/TwoArgOperationResponse" message="tns:IContract_TwoArgOperation_OutputMessage"/>
</wsdl:operation>
<wsdl:operation name="MultiArgsOperation">
<wsdl:input wsaw:Action="http://tempuri.org/IContract/MultiArgsOperation" message="tns:IContract_MultiArgsOperation_InputMessage"/>
<wsdl:output wsaw:Action="http://tempuri.org/IContract/MultiArgsOperationResponse" message="tns:IContract_MultiArgsOperation_OutputMessage"/>
</wsdl:operation>
<wsdl:operation name="OperationWithReturnArg">
<wsdl:input wsaw:Action="http://tempuri.org/IContract/OperationWithReturnArg" message="tns:IContract_OperationWithReturnArg_InputMessage"/>
<wsdl:output wsaw:Action="http://tempuri.org/IContract/OperationWithReturnArgResponse" message="tns:IContract_OperationWithReturnArg_OutputMessage"/>
</wsdl:operation>
</wsdl:portType>
這就是全部定義了嗎?它甚至連每個服務操作的簽名信息都不齊全呢,很明顯這不是全部的內容,更多細節的WSDL馬上就發出來,但在此之前,首先看下這部分的WSDL有什么有趣的地方也好。
1.最外層的節點<wsdl:portType>,在上面的WSDL中,它只有一個屬性name被設置為"IContract”,沒錯,這個節點就是代表這我所發布的服務契約,而它的name屬性被默認的設置為接口的直接名稱(而不是連同命名空間的完整名稱)。
2.在portType節點內部的5個<wsdl:operation>節點,很明顯,它們的name屬性的值,就告知所有人,它們代表著我在服務契約中定義的5個被公開做服務操作的方法。?
3.在operation節點里面的<wsdl:input>和<wsdl:output>節點。在WSDL中,這兩個節點的順序就可以代表著一種消息交換模式(Message Exchange Pattern MEP),例如這里使用的是一個input,一個output的標準請求-響應模式。這個定義就意味著,當調用這個服務操作的時候,會首先從客戶端發送一個"input"消息;操作結束后服務端會生成一個"output"消息,并返回到客戶端,往后還會看到其他的MEP,但它們都是利用這些input、output節點的定義來說明的。
4.無論是input還是output節點,都設置了Action屬性,這個屬性很重要,它代表了調用服務過程中,往來的消息(可能從Server到Client,也可能相反)各自代表了怎么樣的“動作”。例如OperationWithReturnArg這個操作,當一個發送到服務端的消息的首部設置有Action="http://tempuri.org/IContract/OperationWithReturnArg"的時候,WCF知道如果把該消息分發到相應的服務操作中,這也就說明了,這個Action首部可以被用于消息篩選。而作為響應的消息,它的Action首部的值被默認設置為:請求消息的Action首部值 + "Response"。
同樣在input和output節點中,均有一個message屬性,被設置為:"tns:<portType名稱>_<operation名稱>_<(Input/Ouput)Message>",這是什么呢?這就是對定義這些操作的細節的XML Schema的引用。接下來就發出這些XML Schema的內容:(為了避免太難看,我以操作為單位,逐個發出它們的XML Schema代碼段,but keep it in mind,這些代碼是在同一份XML Schema文件中的。)
?? 首先是NoArgsOperation,它的XML Schema代碼是最簡單的,因為沒有返回值,也沒有參數:
[OperationContract]void NoArgsOperation(); 代碼 <xs:element name="NoArgsOperation">
<xs:complexType>
<xs:sequence/>
</xs:complexType>
</xs:element>
<xs:element name="NoArgsOperationResponse">
<xs:complexType>
<xs:sequence/>
</xs:complexType>
</xs:element>
這段XML代碼中,定義了兩個<xs:element>節點,從它們的name屬性馬上就可以推斷出,它們是對應操作NoArgsOperation的一種XML自定義元素(我先把它們分別稱作負載元素)。這兩個負載元素中沒有任何有趣的內容,因為這個操作本來就沒有返回值和參數。
接下來看一下為OneArgOperation操作所定義的XML元素:
[OperationContract]void OneArgOperation(int arg1); 代碼 <xs:element name="OneArgOperation">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="arg1" type="xs:int"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="OneArgOperationResponse">
<xs:complexType>
<xs:sequence/>
</xs:complexType>
</xs:element>
這個操作只有一個參數,同樣沒有返回值。在代表請求負載的負載元素中的<xs:sequence>孫節點中(因為中間還有個<xs:complexType)子節點),又出現了一個XML自定義元素(這個我稱作參數元素),參數元素有三個屬性——minOccurs、name、type,回顧一下前面的服務契約C#代碼,就會知道,這三個屬性都是對操作方法中參數的描述,分別是:請求消息中,代表該參數的XML節點至少出現多少個、該參數的名稱(很明顯默認是使用了程序代碼中的名稱arg1)、該參數的類型。這里有一個很奇怪的地方,那就是允許參數最少出現的次數為0,而事實上這個參數是必須要提供的,在生成的客戶端代理中,也必須提供這個參數。
接下來是兩個參數的操作TwoArgOperation對應的消息元素定義:
[OperationContract]void TwoArgOperation(int arg1, string arg2); 代碼 <xs:element name="TwoArgOperation">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="arg1" type="xs:int"/>
<xs:element minOccurs="0" name="arg2" nillable="true" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="TwoArgOperationResponse">
<xs:complexType>
<xs:sequence/>
</xs:complexType>
</xs:element>
從該操作的負載元素定義可以看出,<xs:sequence>節點所包含的就是負載攜帶的參數的列表。這個操作中,arg2的CLR類型為string,由于默認可以為null,因此設置了參數元素的nillable屬性為true。這個操作在響應消息元素中同樣沒有什么有趣的地方。
下面是MultiArgsOperation操作的消息元素定義:
[OperationContract]void MultiArgsOperation(params double[] args); 代碼 <xs:element name="MultiArgsOperation">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="args" nillable="true" type="q1:ArrayOfdouble" xmlns:q1="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="MultiArgsOperationResponse">
<xs:complexType>
<xs:sequence/>
</xs:complexType>
</xs:element>
在服務契約的C#代碼中,我使用了C#的params關鍵字來定義該操作的參數可以為任意多個double類型的值。其實最后這個可變長參數只是作為一個數組來使用(或者說它其實就是一個數組),當然這在元數據中需要特殊對待,但在編程的時候真的使用一個數組參數沒什么區別。因此,在XML中定義的負載元素,也是把該參數元素的類型定義為一個"ArrayOfdouble",這個名字很奇怪,很明顯,它不是微軟提供的XML預定義數據類型,那這樣就必定在某處WSDL中存在這個XML數據類型的定義。關于這個內容,在后面再研究。
繼續看下一個操作——OperationWithReturnArg:
[OperationContract]object OperationWithReturnArg(object argObj1); 代碼 <xs:element name="OperationWithReturnArg">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="argObj1" nillable="true" type="xs:anyType"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="OperationWithReturnArgResponse">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="OperationWithReturnArgResult" nillable="true" type="xs:anyType"/>
</xs:sequence>
</xs:complexType>
</xs:element>
這個操作是具有一個參數和CLR的object類型返回值的,參數已經沒有什么值得研究的地方,看下該操作的響應負載元素的定義,所攜帶的參數的類型為xs:anyType,在CLR中,object類型是一切類型的基類,任何類型(值類型或引用類型)的對象都可以安全轉換為object類型來處理,因此在XML標準數據類型中,可以使用這個anyType來代表object類型的數據。
值得一提的是,服務操作的返回值的定義方式,跟之前看到的操作參數定義方式完全一樣。前面已經提到,WSDL標準中,使用input、output兩個XML元素來表示一個服務操作的MEP,所以盡管我在服務契約的C#代碼中只是定義了一個方法,它具有參數和返回值,但參數跟返回值只是程序語言上的概念;當我為服務操作定義的MEP是請求-響應模式時,在WSDL中就已經把這個單一的方法分解成兩個消息傳輸模式(一個input,一個output),無論是哪個消息傳輸模式,它所代表的都是消息的傳輸,而對于消息來說,沒有所謂的返回值,只有攜帶的參數。因此在服務操作對應的負載元素(請求消息負載和響應消息負載)定義中,操作中的參數和返回值都是以一致的方式被定義,因為它們都只是消息所攜帶的參數(內容)。
還記得剛才的"ArrayOfdouble" ,現在來研究下這個數據類型到底怎么出來的。相信有XML基礎的讀者都知道,XML具有高擴展性,可以自定義各種標記和XML數據類型,而通常由XML Schema來描述XML文件中各種標記或XML數據類型的細節。那么在所生成WSDL中,在哪里引用外部的(或者生成的)XML Schema呢?先看下下面的WSDL代碼:
代碼 <wsdl:types><xsd:schema targetNamespace="http://tempuri.org/Imports">
<xsd:import schemaLocation="http://localhost:8090/mex?xsd=xsd0" namespace="http://tempuri.org/"/>
<xsd:import schemaLocation="http://localhost:8090/mex?xsd=xsd1" namespace="http://schemas.microsoft.com/2003/10/Serialization/"/>
<xsd:import schemaLocation="http://localhost:8090/mex?xsd=xsd2" namespace="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/>
</xsd:schema>
</wsdl:types>
在<wsdl:types>節點中定義了所有該WSDL所引用的XML Schema,其中包括根據服務契約生成的XML Schema(上面代碼中的第一個<xsd:import>)和微軟預定義的XML Schema(上面代碼中的后兩個<xsd:import>)。同時這個被定義的XML Schema通過targetNamespace屬性,被導入到http://tempuri.org/Imports命名空間中。命名空間為http://tempuri.org/的XML Schema文件其實就是定義之前所有負載元素的文件,里面根據每個服務操作的MEP,定義所需的負載元素。而第二個<xsd:import>所導入的XML Schema就是微軟定義的一些基礎XML數據類型。而最后一個<xsd:import>所導入的XML Schema就是"ArrayOfdouble"的定義,下面發出其代碼:
代碼 <?xml version="1.0" encoding="utf-8"?><xs:schema elementFormDefault="qualified" targetNamespace=http://schemas.microsoft.com/2003/10/Serialization/Arrays xmlns:xs=http://www.w3.org/2001/XMLSchema xmlns:tns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<xs:complexType name="ArrayOfdouble">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" name="double" type="xs:double"/>
</xs:sequence>
</xs:complexType>
<xs:element name="ArrayOfdouble" nillable="true" type="tns:ArrayOfdouble"/>
</xs:schema>
一個值得注意的地方是,這個XML Schema定義了"ArrayOfdouble"后,是把它的定義放入到http://schemas.microsoft.com/2003/10/Serialization/Arrays中,而不是本服務的命名空間中(例如http://tempuri.org/)。
前面說過,WCF自動為每個服務操作生成對應的負載元素,那在WSDL中是怎么把服務操作元素<wsdl:operation>與負載元素<xs:element>關聯起來呢?答案是通過<wsdl:message>元素。以MultiArgsOperation為例:
代碼 <wsdl:operation name="MultiArgsOperation"><wsdl:input wsaw:Action=http://tempuri.org/IContract/MultiArgsOperation message="tns:IContract_MultiArgsOperation_InputMessage"/>
<wsdl:output wsaw:Action=http://tempuri.org/IContract/MultiArgsOperationResponse message="tns:IContract_MultiArgsOperation_OutputMessage"/>
</wsdl:operation>
<wsdl:message name="IContract_MultiArgsOperation_InputMessage">
<wsdl:part name="parameters" element="tns:MultiArgsOperation"/>
</wsdl:message>
<wsdl:message name="IContract_MultiArgsOperation_OutputMessage">
<wsdl:part name="parameters" element="tns:MultiArgsOperationResponse"/>
</wsdl:message>
<xs:element name="MultiArgsOperation">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="args" nillable="true" type="q1:ArrayOfdouble" xmlns:q1="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="MultiArgsOperationResponse">
<xs:complexType>
<xs:sequence/>
</xs:complexType>
</xs:element>
從上面的WSDL代碼,簡單總結就是:服務操作定義了消息的輸入管道元素、輸出管道元素及其順序;每個輸入/輸出管道元素引用了一個消息元素;每個消息元素包裝著它的負載元素。
這篇文章還沒有提及對各種契約進行元數據配置的情況,還記得我用的服務契約(包括其操作契約)是完全沒有經過定制配置的。下一篇文章將嘗試進行一些定制配置,然后研究一下WSDL會有什么相應的變化。
補充 :
在xml中,定義了namespace,如果要驗證xml文檔的有效性,需要這些namespace相關的xsd文檔。
?
引入xsd有的方式:
通過schemaLocation方式:
?
1.xsi:schemaLocation
?
Sql代碼???這個例子是從這里來的,
?
?
簡單說明一下:
- books的缺省命名空間:“http://www.sunxin.org/bks”
- 又定義了一個前綴p: xmlns:p=“http://www.sunxin.org/people” (如果不需要有xsd對應,可以隨意定義;如果需要驗證,這個namespace必須和xsd中的TargetNamespace一致)
- xmlns:xsi是xml標準的一個前綴,作用在xsi:schemaLocation中體現出來
- xsi:schemaLocation中引入對個URI對,
- 第一個為xsd的TargetNamespace(這個必須和后面的xsd中的TargetNamespace匹配),
- 第二個代表從哪里找到這個xsd文件
- 在schemaLocation中,可以出現多個類似的匹配對(URI URL )
- 這樣前面第一個前綴,通過定義的namespace和schemaLocation中的xsd就可以對應起來了;
?2.前綴:schemaLoaction
?
?
Java代碼???
?說明:
- 定義了前綴other,然后申明other前綴的xsd
3.import 和 include
?import: 可以用于不同的命名空間
?include: 必須是同一命名空間
?
Xml代碼???
??
轉載于:https://www.cnblogs.com/f204eng/archive/2012/07/10/2584610.html
總結
- 上一篇: 【转载】[OS X笔记]安装MacPor
- 下一篇: BEx Query Designer中的