元数据实际上是服务终结点的描述,终结点由地址(Address)、绑定(Binding)和契约(Contract)经典的ABC三要素组成。认真阅读过《WCF技术剖析(卷1)》的读者相对会对这三要素的本质有一个深刻的认识:地址决定了服务的位置并实现相应的寻址机制;契约描述了消息交换模式(Message Exchange Pattern: MEP)以及消息的结构(Schema);绑定则通过创建信道栈实现对消息的编码、传输和基于某些特殊的功能(比如实现事务、可靠传输以及基于消息的安全)对消息作出的处理。
服务的消费者通过获取服务端发布的元数据,并在此基础上重建终结点,才能取保请求:消息被发送到准确的目标地址;采用服务端期望的消息交换模式和并生成服务端能够识别的消息结构;使用相匹配的消息编码方式以确保服务端能够对接收到的消息进行正常解码;使用一致的传输协议以实现消息的正常传输;对消息进行与服务端一致性的处理以确保对事务、可靠传输、消息安全等协议的实现。
WCF是基于SOA构建的一个分布式通信平台,而SOA一个重要的特性就是实现跨平台的互操作。元数据是确保服务消费者正常调用目标服务(可能部署于异质平台),所以元数据本身需要采用一种开放的标准来表示。目前,元数据具有三种比较典型的表示方式:
- XSD:通过XML Schema的形式描述组成消息的数据类型的XML结构;
- WSDL:通过一个完整的Web Service Description Language文档对服务进行全面的描述,即包括抽象的功能,也包括具体的细节;
- WS-Policy策略:通过WS-Policy规范以断言(Assertion)形式对服务能力和特性进行描述。
对跨平台互操作的实现不仅仅要求承载服务描述信息的元数据本身采用一种开放的标准或者规范来表示,甚至要求元数据交换(Metadata Exchange:MEX)同样按照厂商共同遵守的规范来进行。在WS-*规范体系中,WS-Metadata Exchange(WS-MEX)为元数据的交换进行了标准化的规范。WS-MetadataExchange(以下简称WS-MEX)规范了与终结点(这里是广义的Web服务终结点,与具体的技术无关)如何表示成一个WS-Transfer资源,并被嵌入到WS-Addressing终结点引用(Endpoint Reference)中,以及元数据如何被相应的Web服务终结点获取。简言之,WS-MEX是一个关于如何进行元数据交换的WS规范。WS-MEX和其他的WS-*规范一起,比如WSDL、WS-Addressing、WS-Transfer、WS—Policy等一起组成了一个完整的描述Web服务元数据和元数据交换的规范体系,在正式介绍WS-MEX之前,先来大概了解一些其他的这些辅助性WS-*规范。
一、WS-Policy
一个Web服务(这里指广义的、与技术平台无关的Web服务)除了实现通过服务契约定义的业务功能之外,为了实现一些额外的功能(比如安全、事务和可靠传输等),还需要具有一些与业务无关的行为(Behavior)和能力(Capability),我们可以将这些统称为Web服务的策略(Policy)。WS-Policy提供了一个基于XML的框架模型和语法用于描述Web服务的能力、要求和行为属性。
WS-Policy属于WS-*体系中的一个基础性规范,其规范本身不会被单独使用,而是服务于其他的WS规范(我们一般称这些为Domain Specific规范,比如WS-Transaction、WS-Reliable Messaging和WS-Security等)提供一种统一的策略描述。
W3C先后在2006年和2007年推出了两个版本的WS-Policy规范,即WS-Policy 1.2和WS-Policy 1.5。在这里,我们仅仅是正对最新的WS-Policy版本(1.5)来简单介绍一些一个完整的WS策略具有怎样的结构,对于希望深入了解WS-Policy的读者,可以通过后面的地址下载到W3C的官方文档:http://www.w3.org/TR/ws-policy/。
WS-Policy采用一个基于XML的策略表达式(Policy Expression)表示一个策略,在投入到对具体策略定义的介绍之前,我们不妨先来看看一个典型的策略表达式的定义。下面XML片断表示的策略表达式来自于WS-Security,这是一个基于如何实现基于消息安全的WS规范。它体现的含义是:对消息的主题部分采用签名还是加密。
1: (01)<wsp:Policy
2: xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"
3: xmlns:wsp="http://www.w3.org/ns/ws-policy" >
4: (02) <wsp:ExactlyOne>
5: (03) <wsp:All>
6: (04) <sp:SignedParts>
7: (05) <sp:Body/>
8: (06) </sp:SignedParts>
9: (07) </wsp:All>
10: (08) <wsp:All>
11: (09) <sp:EncryptedParts>
12: (10) <sp:Body/>
13: (11) </sp:EncryptedParts>
14: (12) </wsp:All>
15: (13) </wsp:ExactlyOne>
16: (14) </wsp:Policy>
上面这个XML采用一种标准的形式(WS-Policy还定义了一种简写的策略表示方式)定义一个策略,接下来我们就此为基础介绍一个完整的策略表达式具有怎样的结构。
在一个基于Web服务的系统中,策略体现的对Web服务相关实体要求(Requirements)、能力(Capabilities)和特性(Characteristics)等行为属性的表示。WS-Policy通过断言(Assertion)的形式来表示这些单一的行为属性,然后通过一定的规则将相关的策略断言有机的组合在一起,以实现对整个Web服务目标实体的完整描述。
按照WS-Policy 1.5的规定,所有的策略元素均定义在http://www.w3.org/ns/ws-policy命名空间下,一个完整策略通过一个基于XML的策略表达式描述。1、
1、策略表达式(Policy Expression)
一个策略表达式是一个XML信息集(XML InfoSet),描述了一个完整的策略。策略表达式具有两个不同的表示形式:标准形式(Normal Form)和简写形式(Compact Form)。一个策略表达式是一个策略选择项(Policy Alternative)的集合。我们要求满足某个策略,意味着我们须要满足该集合的至少一个策略选择项。
2、策略选择项(Policy Alterative)
一般情况下,策略往往是承载一些确保服务正常调用(说得更加具体一点,就是确保Web服务终结点能够正常交互)的条件信息。服务的消费者在进行正常的服务消费之前,需要保证提供这些必备的前提条件。对于服务提供者来说,针对某个具体的应用场景的时候,会提供一到多个不同的选择项。比如我们上面给出的例子,在应用基于消息的安全策略的时候,根据不同安全级别的需求,可以选择对消息的主体部分进行签名或者加密。
这些单一的选择项被称为策略选择项(Policy Alternative),对于上面给出的策略表达式,(03)-(07)和(08)-(12)定义两个策略选择项,代表对消息主体进行签名还是加密。一个策略选择项由零到多个策略断言通过相应的策略操作符组合在一起。
3、策略断言(Policy Assertion)
在WS-Policy规范下,Web服务实体某个单一的行为属性最终通过一个策略断言表示。而一个策略断言由一个比如的断言类型(Assert Type)和一组可选的断言参数(Assert Parameter)组成。断言类型通过一个有效名称(Qualified Name: QName)表示,即命名空间和本地名称(Local Name)的组合。在定义了两个策略断言:(04)-(06)和(09)-(11),其断言类型分别为:sp:SignedParts和sp:EncryptedParts。
一个最简单的策略断言可以仅仅由一个包含断言类型的空XML元素构成,我们也可以为这个XML元素添加用于辅助描述该断言的XML属性(Attribute)和XML子元素,我们把这些策略辅助描述信息称为断言参数。
一个策略断言可以很简单(一个空XML元素),也可以定义的很复杂,这取决于具体的策略描述对象。一个比较极端的策略断言是:将一个完整的策略表达式作为其子元素,我们把这种情况称为策略断言嵌套(Policy Assetion Nesting)。嵌套的策略断言的结构可以通过下面的XML表示:
1: (01) <Assertion …>
2: (02) …
3: (03) ( <wsp:Policy …> … </wsp:Policy> )?
4: (04) …
5: (05) </Assertion>
4、策略操作符(Policy Operator)
一个策略选择项又由零(也就是说可以定义空的策略选择项)到多个策略断言通过一定的规则构成,在这里对策略断言的组合规则通过策略操作符来体现。策略操作符体现的是这样一种含义:请求者采用怎样的方式去满足构成策略选择项的所有策略断言,需要满足所有的断言呢,还是仅仅需要满足其中某一个?
WS-Policy定义了两种主要的策略操作符(实际上Policy本身就属于一个策略操作符)ExactlyOne和All。根据名称我们不难猜出,ExactlyOne表示仅仅需要满足断言集合的某一个元素即可,而All意味着需要满足断言集合中的所有元素。
此外,由于wsp:ExactlyOne表示的是“满足其中之一”的意思,这和策略和策略选择项之间体现的关系吻合,所以在标准形式体现的策略表达式中,所有的策略选择项均纳于wsp:ExactlyOne操作符之中。
图1 策略表达式结构图
整个策略表达式的结构,即策略、策略选择项和策略断言之间的关系,大体可以通过图1表示。关于WS-Policy中对策略表达式的规定,还一个其他一些额外的内容,比如策略的识别(Policy Identifying)、策略简写形式(Compact Form)等,在这里就不再一一介绍了,有兴趣的读者可以下载WS-Policy 1.5的官方文档(http://www.w3.org/TR/ws-policy/)。
二、WS-Transfer
在Web服务的世界中,很多资源(Resource)都可以通过XML的形式来表示,并通过WS-Addressing规范的方式进行寻址。而WS-Transfer就是这样的一个WS规范:规定如何采用基于SOAP的方式实现可寻址的(Addressable)Web服务资源的获取、更新、删除和创建。接下来对WS-Transfer的介绍,假设你对WS-Addressing和SOAP有了一个基本的了解。对这两个规范不是很熟悉的读者,可以从W3C的网站上下载官方文档。此外,在《WCF技术剖析(卷1)》的第2章和第6章对WS-Addressing 1.0和SOAP 1.2进行了概括性的介绍。
虽然我们可以通过很多不同的方式(比如REST)对资源进行获取和更新,WS-Transfer完全建立在基于SOAP基础上。WS-Transfer的主要内容集中在对4个基本资源操作上面:Get、Put、Delete和Create,分别实现对资源的获取、更新、删除和创建。W3C分别在2006年3月份和9月份先后推出了两个版本的WS-Transfer,我们接下来的介绍完全基于最新版本的WS-Transfer,你可以从后面的地址获取正式的官方文档:http://www.w3.org/Submission/WS-Transfer/。
所以基于WS-Transfer元素定义在http://schemas.xmlsoap.org/ws/2004/09/transfer命名空间下。接下来我们着重对上述的4个资源进行介绍。
1、资源的获取:Get
请求者创结基于Get操作的SOAP消息像目标地址发送请求以获取相应的资源,这样的请求消息必须具有如下的格式。
1: <s:Envelope …>
2: <s:Header …>
3: <wsa:Action>
4: http://schemas.xmlsoap.org/ws/2004/09/transfer/Get
5: </wsa:Action>
6: <wsa:MessageID>xs:anyURI</wsa:MessageID>
7: <wsa:To>xs:anyURI</wsa:To>
8: …
9: </s:Header>
10: <s:Body ...>
11: …
12: </s:Body>
13: </s:Envelope>
WS-Addressing报头Action的值必须是http://schemas.xmlsoap.org/ws/2004/09/transfer/Get,消息的主体(Body)部分为空。资源接受Get请求后,采用如下结构的SOAP消息进行回复,Action报头为http://schemas.xmlsoap.org/ws/2004/09/transfer/GetResponse,表示资源的XML必须作为消息主体的第一个子元素。
1: <s:Envelope …>
2: <s:Header …>
3: <wsa:Action>
4: http://schemas.xmlsoap.org/ws/2004/09/transfer/GetResponse
5: </wsa:Action>
6: <wsa:RelatesTo>xs:anyURI</wsa:RelatesTo>
7: <wsa:To>xs:anyURI</wsa:To>
8: …
9: </s:Header>
10: <s:Body …>
11: xs:any
12: …
13: </s:Body>
14: </s:Envelope>
2、资源的更新:Put
请求者创建基于Put操作的SOAP消息,提供新的资源内容向目标地址发送请求,以更新某个现有的资源。Put请求消息必须具有如下的格式:
1: <s:Envelope …>
2: <s:Header …>
3: <wsa:Action>
4: http://schemas.xmlsoap.org/ws/2004/09/transfer/Put
5: </wsa:Action>
6: <wsa:MessageID>xs:anyURI</wsa:MessageID>
7: <wsa:To>xs:anyURI</wsa:To>
8: …
9: </s:Header>
10: <s:Body…>
11: xs:any
12: ...
13: </s:Body>
14: </s:Envelope>
Put请求消息的Action报头为http://schemas.xmlsoap.org/ws/2004/09/transfer/Put,表示资源的XML必须作为消息主体的第一个子元素。资源接受Put请求后,通过具有如下格式的SOAP回复请求。回复消息的Action报头为http://schemas.xmlsoap.org/ws/2004/09/transfer/PutResponse,如何完全采用请求者提供的资源对现有的目标资源进行更新,那么回复消息的主体部分为空,否则将更新后的资源以XML的形式置于回复消息主体部分的第一个子元素中。
1: <s:Envelope …>
2: <s:Header …>
3: <wsa:Action>
4: http://schemas.xmlsoap.org/ws/2004/09/transfer/PutResponse
5: </wsa:Action>
6: <wsa:RelatesTo>xs:anyURI</wsa:RelatesTo>
7: <wsa:To>xs:anyURI</wsa:To>
8: …
9: </s:Header>
10: <s:Body …>
11: xs:any ?
12: </s:Body>
13: </s:Envelope>
3、资源的删除:Delete
请求者创建基于Delelte操作的SOAP消息向目标地址发送请求,以删除某个现有的资源。Delelte请求消息必须具有如下的格式:
1: <s:Envelope …>
2: <s:Header …>
3: <wsa:Action>
4: http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete
5: </wsa:Action>
6: <wsa:MessageID>xs:anyURI</wsa:MessageID>
7: <wsa:To>xs:anyURI</wsa:To>
8: …
9: </s:Header>
10: <s:Body … />
11: </s:Envelope>
Delete请求消息的Action报头为http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete,消息主体部分为空。资源接受Delete请求后,通过具有如下格式的SOAP回复请求。回复消息的Action报头为http://schemas.xmlsoap.org/ws/2004/09/transfer/DeleteResponse,回复消息的主体部分为空。
4、资源的创建:Create
请求者创建基于Create操作的SOAP消息向目标地址发送请求,以创建一个新的资源。Create请求消息必须具有如下的格式:
1: <s:Envelope …>
2: <s:Header …>
3: <wsa:Action>
4: http://schemas.xmlsoap.org/ws/2004/09/transfer/Create
5: </wsa:Action>
6: <wsa:MessageID>xs:anyURI</wsa:MessageID>
7: <wsa:To>xs:anyURI</wsa:To>
8: …
9: </s:Header>
10: <s:Body …>
11: xs:any
12: ...
13: </s:Body>
14: </s:Envelope>
Create请求消息的Action报头为http://schemas.xmlsoap.org/ws/2004/09/transfer/Create,新的资源内容以XML的形式作为消息主体部分的第一个子元素。资源工厂接受Create请求后,通过具有如下格式的SOAP回复请求。回复消息的Action报头为http://schemas.xmlsoap.org/ws/2004/09/transfer/CreateResponse,并将新创建资源的WS-Addressing的终结点引用(Endpoint Reference)作为回复消息主体部分的第一个子元素。如果,最终被更新的资源内容和请求者提供的不一致,本更新的资源内容需要作为回复消息主体部分的第二个子元素返回。
1: <s:Envelope …>
2: <s:Header …>
3: <wsa:Action>
4: http://schemas.xmlsoap.org/ws/2004/09/transfer/CreateResponse
5: </wsa:Action>
6: <wsa:RelatesTo>xs:anyURI</wsa:RelatesTo>
7: <wsa:To>xs:anyURI</wsa:To>
8: …
9: </s:Header>
10: <s:Body …>
11: <wxf:ResourceCreated>endpoint-reference</wxf:ResourceCreated>
12: xs:any?
13: </s:Body>
14: </s:Envelope>
三、 WSDL
WSDL,全称Web服务描述语言(Web Service Description Language),是采用XML格式的形式对Web服务的描述。WSDL将一个Web服务定义成一组终结点的集合,而每一个终结点包含一系列基于消息(Message)的操作(Operation)。这些抽象的操作和消息最终和相应的协议以及消息格式绑定。
虽然W3C在2007年6月份就正式出台了WSDL 2.0版本,并将其作为官方推荐,但是该版本并没有得到广泛的推广,并没有被主流的厂商完全支持。如今,WCF完全支持的还是WSDL 1.1版本,所以接下来我们将针对这个版本对WSDL作一个简单的介绍,对于希望了解WSDL1.1的读者可以从后面的地址下载官方文档:http://www.w3.org/TR/wsdl。就WSDL描述对象的性质,我们大体可以将所有WSDL的元素划分为以下两类:
- 抽象元素:比如通过XSD表示的数据类型;用于承载数据信息的消息;通过对关联的消息按照某种消息交换模式组合而成的操作等;
- 具体元素:比如将相应的操作和具体的网络协议和消息格式进行绑定等。
为了有效地了解WSDL的结构,我们首先来看看一段直接从官方文档上拷贝出来的WSDL文档:
1: <?xml version="1.0"?>
2: <definitions name="StockQuote" targetNamespace="http://example.com/stockquote.wsdl" xmlns:tns="http://example.com/stockquote.wsdl" xmlns:xsd1="http://example.com/stockquote.xsd" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns="http://schemas.xmlsoap.org/wsdl/">
3: <types>
4: <schema targetNamespace="http://example.com/stockquote.xsd" xmlns="http://www.w3.org/2000/10/XMLSchema">
5: <element name="TradePriceRequest">
6: <complexType>
7: <all>
8: <element name="tickerSymbol" type="string"/>
9: </all>
10: </complexType>
11: </element>
12: <element name="TradePrice">
13: <complexType>
14: <all>
15: <element name="price" type="float"/>
16: </all>
17: </complexType>
18: </element>
19: </schema>
20: </types>
21: <message name="GetLastTradePriceInput">
22: <part name="body" element="xsd1:TradePriceRequest"/>
23: </message>
24: <message name="GetLastTradePriceOutput">
25: <part name="body" element="xsd1:TradePrice"/>
26: </message>
27: <portType name="StockQuotePortType">
28: <operation name="GetLastTradePrice">
29: <input message="tns:GetLastTradePriceInput"/>
30: <output message="tns:GetLastTradePriceOutput"/>
31: </operation>
32: </portType>
33: <binding name="StockQuoteSoapBinding" type="tns:StockQuotePortType">
34: <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
35: <operation name="GetLastTradePrice">
36: <soap:operation soapAction="http://example.com/GetLastTradePrice"/>
37: <input>
38: <soap:body use="literal"/>
39: </input>
40: <output>
41: <soap:body use="literal"/>
42: </output>
43: </operation>
44: </binding>
45: <service name="StockQuoteService">
46: <documentation>My first service</documentation>
47: <port name="StockQuotePort" binding="tns:StockQuoteBinding">
48: <soap:address location="http://example.com/stockquote"/>
49: </port>
50: </service>
51: </definitions>
上面给出的是一个很标准的WSDL文档,从中我们可以看出它由五个子元素构成:Type、Message、PortType、Binding和Service。这五大元素构成一个了一个完成得WSDL,现在可以就对它们逐个进行介绍。
1、Types:通过XSD表示的数据类型的集合
WSDL并没有属于自己的数据类型定义规范,而是直接采用XSD作为数据定义的语言。上面的WSDL文档通过XSD定义了两个XML元素,元素名称分别为TradePriceRequest和TradePrice,命名空间为http://example.com/stockquote.xsd。从XSD的定义我们不难看出,这两个类型分别是字符串和浮点数类型。
1: <types>
2: <schema targetNamespace="http://example.com/stockquote.xsd" xmlns="http://www.w3.org/2000/10/XMLSchema">
3: <element name="TradePriceRequest">
4: <complexType>
5: <all>
6: <element name="tickerSymbol" type="string"/>
7: </all>
8: </complexType>
9: </element>
10: <element name="TradePrice">
11: <complexType>
12: <all>
13: <element name="price" type="float"/>
14: </all>
15: </complexType>
16: </element>
17: </schema>
18: </types>
2、Message: 通信数据的载体
Web服务采用基于消息的通信方式,所以消息是通信数据的载体。WSDL的message元素用于定义所有定义终结点操作的消息的结构。WSDL的消息是一个具有唯一标识(通过Name属性)的XML元素,通常利用Types结点中定义的数据类型来描述。上面的WSDL定义了两个消息,名称分别为GetLastTradePriceInput和GetLastTradePriceOutput,消息主体部分的结构通过引用定义在Types结点中的XML元素的有效名称(QName:命名空间+本地名称)。
1: <message name="GetLastTradePriceInput">
2: <part name="body" element="xsd1:TradePriceRequest"/>
3: </message>
4: <message name="GetLastTradePriceOutput">
5: <part name="body" element="xsd1:TradePrice"/>
6: </message>
3、PortType:相关操作的集合
一个服务逻辑上有一系列关联的操作组成,从消息交换的角度上讲,操作进行关联的消息按照相应的消息交换模式的有机组合。WSDL的PortType表示的就是这么一个操作的集合,反映在XML结构上,就是一组operation元素的基本。每一个operation XML元素代表一个单一的操作,它通过一个或者多个消息组合而成。消息的不同组合方式反映了操作采用的不同消息交换模式(MEP: Message Exchange Pattern)。上面给出的WSDL通过如下的XML片断定义了一个仅仅包含一个操作的PortType。
1: <portType name="StockQuotePortType">
2: <operation name="GetLastTradePrice">
3: <input message="tns:GetLastTradePriceInput"/>
4: <output message="tns:GetLastTradePriceOutput"/>
5: </operation>
6: </portType>
在《WCF技术剖析(卷1)》第4章对服务操作的介绍中,我们说服务契约中的操作契约本质上就是定义了操作采用的消息交换模式,以及消息的格式。在这里你会进一步得到证实,实际上,当某个WCF服务通过WSDL的形式发布出来,服务契约映射的部分就是PortType。WCF支持三种典型的消息交换模式:单工(One-way)请求回复(Request-Reply)和双工(Duplex)。实际上,双工模式是由前面两种模式组合而成,单工(One-way)和请求-回复(Request-Reply)模式才是基元消息交换模式。除了这两种基元模式,WSDL还对另外两种消息交换模式提供支持:恳请-回复(Solicit-Response)和通知(Notification)模式。
PortType中的每一个操作均由输入(Input)和输出(Output)消息的不同组合方式定义,而这种对输入、输出消息的不同组合就是对某种消息交换模式的反映。接下来,我们站在服务端终结点的角度,来介绍上述的4中消息交换模式:
单工(One-way)
单工消息交换模式下,终结点仅仅是接收来自客户端的请求。单向操作仅仅包含一个输入消息,在WSDL中的表示如下:
1: <wsdl:definitions ... >
2: <wsdl:portType ... >*
3: <wsdl:operation name="nmtoken">
4: <wsdl:input name="nmtoken"? message="qname"/>
5: </wsdl:operation>
6: </wsdl:portType >
7: </wsdl:definitions>
请求-回复(Request-Reply)
请求-回复消息交换模式下,终结点接收来自客户端的请求,并向发送匹配的回复消息。请求-恢复操作通过输入消息和输出消息的有序组合表示,在WSDL中的表示如下:
1: <wsdl:definitions …>
2: <wsdl:portType …>*
3: <wsdl:operation name="nmtoken" parameterOrder="nmtokens">
4: <wsdl:input name="nmtoken"? message="qname"/>
5: <wsdl:output name="nmtoken"? message="qname"/>
6: <wsdl:fault name="nmtoken" message="qname"/>*
7: </wsdl:operation>
8: </wsdl:portType>
9: </wsdl:definitions>
恳请-回复(Solicit-Response)
在恳请-回复消息交换模式下,终结点先向客户端到发送请求,并接收来自客户端的回复,这和请求-回复模式正好相反。恳请-回复操作由输出消息和输入消息的有序组合表示,在WSDL中的表示如下:
1: <wsdl:definitions …>
2: <wsdl:portType …>*
3: <wsdl:operation name="nmtoken" parameterOrder="nmtokens">
4: <wsdl:output name="nmtoken"? message="qname"/>
5: <wsdl:input name="nmtoken"? message="qname"/>
6: <wsdl:fault name="nmtoken" message="qname"/>*
7: </wsdl:operation>
8: </wsdl:portType>
9: </wsdl:definitions>
通知(Notification):
在通知消息交换模式下,终结点仅仅向客户端发送请求,这和单向模式正好相反。通知操作由单一的输出消息组成,在WSDL中的表示如下:
1: <wsdl:definitions …>
2: <wsdl:portType …> *
3: <wsdl:operation name="nmtoken">
4: <wsdl:output name="nmtoken"? message="qname"/>
5: </wsdl:operation>
6: </wsdl:portType>
7: </wsdl:definitions>
注:对于请求-回复和恳请-回复消息交换模式来说,双方除了进行正常的消息交换之外,当错误发生,需要将错误信息封装成消息发送给对方。所以,这两种类类型的操作除了输出消息和输入消息的描述之外,还具有错误消息的描述。错误消息在操作中通过<wsdl:fault/〉表示。
4、Bindings:消息、操作与协议、格式的绑定
上面介绍WSDL的三个元素主要从抽象的角度对数据类型、消息和操作进行描述,要创建服务于具体消息交换场景的终结点,还需要将这需抽象的描述和具体的消息格式(Format)和网络协议绑定,比如SOAP、HTTP-GET和MIME等。
在这里,我们很有必要强调“终结点”,本节我们提到的终结点在大部分场景中都是指与技术无关的、用于进行消息交换的“端口”。而我们WCF中提到的终结点,可以看成是这样一个通用的终结点在具技术平台中的实现。WCF的终结点由地址、绑定和契约构成,结合WSDL我们不难看出,Type、Message和PortType是对契约的描述。而绑定,就其语言和功能上讲,就是实现了抽象的描述和具体的协议(网络传输协议、SOAP和WS-*规范等)之间的绑定。在WSDL中,Bindings元素具有一样的功能。
在WSDL中,我们可以通过很多绑定扩展实现与某种协议的绑定,而最为常见的是基于SOAP 1.1和SOAP 1.2的绑定。上面给出的WSDL中定义了一个典型的基于SOAP 1.1的绑定(SOAP 1.1和SOAP 1.2绑定的命名空间分别为http://schemas.xmlsoap.org/wsdl/soap/和http://schemas.xmlsoap.org/wsdl/soap12/)。
1: <binding name="StockQuoteSoapBinding" type="tns:StockQuotePortType">
2: <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
3: <operation name="GetLastTradePrice">
4: <soap:operation soapAction="http://example.com/GetLastTradePrice"/>
5: <input>
6: <soap:body use="literal"/>
7: </input>
8: <output>
9: <soap:body use="literal"/>
10: </output>
11: </operation>
12: </binding>
以上面这个SOAP绑定为例,一个绑定通过name属性(name="StockQuoteSoapBinding")定义其唯一标识,并通过type属性和定义个PortType(type="tns:StockQuotePortType")进行关联。该SOAP绑定定义了消息采用的风格(style="document",另外一个选项是“rpc”)和传输协议(transport="http://schemas.xmlsoap.org/soap/http")。在操作级别,定义了操作反映在SOAP Action报头的值(soapAction="http://example.com/GetLastTradePrice"),以及输入和输出消息的主体部分采用的编码方式(use="literal")。
5、Service:相关终结点的集合
由于一个Web服务最终以终结点的方式暴露出来,所以WSDL最终体现在对终结点集合的描述,这里介绍的WSDL最后一个元素<Service/>本质上就是对基于该Web服务的一组相关终结点的定义。我们照例将上面给出的WSDL的Service相关部分提取出来,根据具体的例子分析Service结点应有的结构。
1: <service name="StockQuoteService">
2: <documentation>My first service</documentation>
3: <port name="StockQuotePort" binding="tns:StockQuoteBinding">
4: <soap:address location="http://example.com/stockquote"/>
5: </port>
6: </service>
<Service>通过name属性(name="StockQuoteService")定义了服务的唯一标识。该节点具有一个可选的<documentation>元素,可以通过单纯的文本或者XML为该服务定义一些可读的说明性的描述。<Service>结点最重要的就是一组<port>元素,而每一个port即代表着一个终结点。每个具有一个name属性定义终结点的名称,通过binding属性引用相应的定义得binding。并通过相应的绑定扩展定义终结点的地址,在这里终结点的地址通过SOAP绑定定义(<soap:address location="http://example.com/stockquote"/>)。
图2反映了WSDL5个元素之间的关系。对于表示一个终结点对象<port>元素来说,它具有一个地址(Address)和关联着一个绑定(Binding),而绑定对象关联着一个PortType。而一个PortType实际上对应的着WCF中的契约(Contract)。所以,WCF下的终结点由地址、绑定和契约三要素组成在这里也得到进一步的反映,实际上,WCF本身就是按照WS开放标准设计的。
图2 WSDL5个元素之间的引用关系
四、 WS-MEX
WCF的元数据结构体系构建在一个开放的标准之上,这个标准就是WS-Metadata Exchange,简称WS-MEX。WS-MEX是WS-*大家庭中的一名重要成员,最新的版本是1.1,希望全面了解WS-MEX 1.1规范的读者可以从后面的地址下载官方文档:http://specs.xmlsoap.org/ws/2004/09/mex/WS-MetadataExchange.pdf。规范了如何将基于Web终结点的元数据表示成一个WS-Transfer资源;如何将元数据内嵌于WS-Addressing的终结点引用(Endpoint Reference)中;以及如何获取某个Web服务终结点的元数据。WS-MEX主要的目的在规范元数据的获取,它提供了如下两种不同的方式去获取Web服务终结点的元数据:WS-Transfer Get和Get Metadata。此外,基于WS-MEX的元素定义在http://schemas.xmlsoap.org/ws/2004/09/mex命名空间下。
1、通过WS-Transfer GET操作获取元数据
我之所以在正式介绍WS-MEX之前会先对WS-Transfer作一个简单的讨论,使因为采用WS-Transfer的Get操作是元数据的主要获取方式之一。通过前面的介绍,我们知道了WS-Transfer旨在规范如何获取、更新、删除和创建Web服务资源。元数据本身就可以作为一种典型的Web服务资源,那么采用WS-Transfer无疑是一种最直接的选择。
元数据的提供者将元数据作为一种Web服务资源通过一个基于WS-Transfer的终结点暴露出来,请求者向该终结点发送WS-Transfer Get请求,以回复消息的形式获得所需的元数据。下面就是一个典型的基于SOAP 1.1的WS-Transfer Get请求消息,请求的目标地址就是元数据资源对应的终结点。
1: <s11:Envelope xmlns:s11="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa10="http://www.w3.org/2005/08/addressing">
2: <s11:Header>
3: <wsa10:Action>http://schemas.xmlsoap.org/ws/2004/09/transfer/Get</wsa10:Action>
4: <wsa10:To>http://services.example.org/stockquote/metadata</wsa10:To>
5: <wsa10:ReplyTo>
6: <wsa10:Address>http://client.example.org</wsa10:Address>
7: </wsa10:ReplyTo>
8: <wsa10:MessageID>urn:uuid:1cec121a-82fe-41da-87e1-3b23f254f128</wsa10:MessageID>
9: </s11:Header>
10: <s11:Body />
11: </s11:Envelope>
针对上面一个基于WS-Transfer Get操作的元数据请求,可能会得到如下一个标准的WS-Transfer Get回复消息,请求的元数据被置于SOAP消息的主体部分。
1: <s11:Envelope xmlns:s11="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa10="http://www.w3.org/2005/08/addressing" xmlns:mex="http://schemas.xmlsoap.org/ws/2004/09/mex" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
2: <s11:Header>
3: <wsa10:Action> http://schemas.xmlsoap.org/ws/2004/09/transfer/GetResponse</wsa10:Action>
4: <wsa10:To>http://client.example.org</wsa10:To>
5: <wsa10:RelatesTo>urn:uuid:1cec121a-82fe-41da-87e1-3b23f254f128</wsa10:RelatesTo>
6: </s11:Header>
7: <s11:Body>
8: <mex:Metadata>
9: <mex:MetadataSection Dialect="http://schemas.xmlsoap.org/wsdl/">
10: <wsdl:definitions name="StockQuote" ...>
11: ...
12: </wsdl:definitions>
13: </mex:MetadataSection>
14: <mex:MetadataSection Dialect="http://www.w3.org/2001/XMLSchema" Identifier="http://services.example.org/stockquote/schemas">
15: <mex:Location>http://services.example.org/stockquote/schemas</mex:Location>
16: </mex:MetadataSection>
17: <mex:MetadataSection Dialect="http://schemas.xmlsoap.org/ws/2004/09/policy" Identifier="http://services.example.org/stockquote/policy">
18: <mex:MetadataReference>
19: <wsa10:Address>http://services.example.org/stockquote/policy</wsa10:Address>
20: </mex:MetadataReference>
21: </mex:MetadataSection>
22: </mex:Metadata>
23: </s11:Body>
24: </s11:Envelope>
从上面提供的包含元数据的SOAP消息中,我们可以看到:所有的元数据均包含在<Metadata>结点中。<Metadata>结点实际上是一个<MetadataSection>元素的集合,具体的元数据定一个在相应的<MetadataSection>结点下,元数据的表现形式通过Dialect属性定义。通过Dialect表示的元数据表示形式被称为元数据方言。
元数据方言(Dialect)
在本章开始的时候,我们就谈到Web服务终结点元数据具有三种典型的表现形式:WSDL、XSD和WS-Policy。在看看我们上面给出的包含元数据的SOAP消息中,<Metadata>结点下三个<MetadataSection>分别就对应这三种形式的元数据。第一个<MetadataSection>通过内联的方式直接嵌入一个WSDL文档;第二个<MetadataSection>以地址的方式指定了一个XML Schema;第三个<MetadataSection>以终结点引用的方式指定了一个WS-Policy策略。<MetadataSection>的Dialect以一个决定地址的形式指明了元数据体现的形式,即元数据方言(Dialect)。图3展现了Metadadata、MetadataSection以及这三种典型元数据方言之间的关系。在WS-MEX中为以下5种方言定义了相应的URI:
- XML Schema:http://www.w3.org/2001/XMLSchema
- WSDL:ttp://schemas.xmlsoap.org/wsdl/
- WS-Policy:http://schemas.xmlsoap.org/ws/2004/09/policy
- WS-Policy Attachment:http://schemas.xmlsoap.org/ws/2004/09/policy/attachment
- MEX:http://schemas.xmlsoap.org/ws/2004/09/mex
注:WS-Policy Attachment是另外一份WS规范,用于如何将WS-Policy中定义的策略和具体的WS规范(Domain-Specific WS Specification)进行关联。MEX代表一般意义的元数据,它包含基于某种具体方言的元数据。
图3 Metata、MetadataSection与三种典型的元数据表现形式
2、通过Get Metadata操作获取元数据
通过WS-Transfer Get的方式获取Web服务终结点的元数据的前提是直接将元数据本身作为Web服务资源。但是对于某些特殊的场景,这种方式不不太适合。比如,多个元数据资源关联到同一个元数据终结点,希望通过向该终结点发送请求获取所有相关的元数据;而且,并不是在任何情况下都能将终结点的元数据作为一个可以被寻址(基于WS-Addressing)的Web服务资源。为了在这些场景中解决元数据的获取,WS-MEX提出了另一种替换的元数据获取方式:Get Metadata。Get Metadata操作请求的SOAP消息具有如下的结构要求(?表示0或1个前置元素):
1: [action]
2: http://schemas.xmlsoap.org/ws/2004/09/mex/GetMetadata/Request
3: [Body]
4: <mex:GetMetadata ...>
5: (<mex:Dialect>xs:anyURI</mex:Dialect>
6: (<mex:Identifier>xs:anyURI</mex:Identifier>)?
7: )?
8: </mex:GetMetadata>
当服务终结点接受了Get Metadata请求后,生成相应的回复消息并将元数据置于消息的主体部分。Get Metadata回复消息具有如下的结构要求:
1: [action]
2: http://schemas.xmlsoap.org/ws/2004/09/mex/GetMetadata/Response
3: [Body]
4: <mex:Metadata ...>
5: ...
6: </mex:Metadata>
出处: http://artech.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。