程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> WCF技術剖析之二十七: 如何將一個服務發布成WSDL[基於HTTP-GET的實現]

WCF技術剖析之二十七: 如何將一個服務發布成WSDL[基於HTTP-GET的實現]

編輯:關於.NET

(提供模擬程序)

基於HTTP-GET的元數據發布方式與基於WS-MEX原理類似,但是ServiceMetadataBehavior需要做的更多額外的工作。原因很簡單,由於在WS-MEX模式下,我們為寄宿的服務添加了相應的MEX終結點,那麼當服務被成功寄宿後,WCF已經為元數據的消息交換建立了如圖1所示的分發體系,我們需要做的僅僅是對MEX終結點的DispatchRuntime進行相應的定制而已。

圖1 WCF服務端分發體系

但是如果采用HTTP-GET模式,實際上我們需要從ChannelDispatcher開始,重新構建整個分發體系。接下來,我們在《WS-MEX原理》提供實例的基礎上,對我們自定義ServiceMetadataBehaviorAttribute進行進一步的完善,使之同時對兩種模式的元數據發布提供支持。 (Source Code從這裡下載)

首先,我們需要定義一個新的服務契約接口:IHttpGetMetadata,Get操作處理任何形式的消息請求,因為它的輸入參數和返回類型均為Message,並且Action和ReplyAction為*。

   1: using System.ServiceModel;
2: using System.ServiceModel.Channels;
3: namespace ServiceMetadataBehaviorSimulator
4: {
5: [ServiceContract(Name = "IHttpGetMetadata", Namespace = "http://www.artech.com/")]
6: public interface IHttpGetMetadata
7: {
8: [OperationContract(Action = "*", ReplyAction = "*")]
9: Message Get(Message msg);
10: }
11: }

然後我們讓前面定義的MetadataProvisionService實現IHttpGetMetadata接口,在這裡無需再寫任何多余的代碼,因為MetadataProvisionService已經具有了一個Get方法。

   1: public class MetadataProvisionService : IMetadataProvisionService, IHttpGetMetadata
2: {
3: //省略成員
4: }

接下來的工作就是構建一個全新的ChannelDispatcher,以及關聯EndpointDispatcher,最後對EndpointDispatcher的DispatchRuntime進行定制。為此,我單獨寫了一個方法:CreateHttpGetChannelDispatcher。

   1: [AttributeUsage(AttributeTargets.Class)]
2: public class ServiceMetadataBehaviorAttribute : Attribute, IServiceBehavior
3: {
4: //其他成員
5: private const string SingletonInstanceContextProviderType = "System.ServiceModel.Dispatcher.SingletonInstanceContextProvider,System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
6: private const string SyncMethodInvokerType = "System.ServiceModel.Dispatcher.SyncMethodInvoker,System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
7: private const string MessageOperationFormatterType = "System.ServiceModel.Dispatcher.MessageOperationFormatter, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
8:
9: private static void CreateHttpGetChannelDispatcher(ServiceHostBase host, Uri listenUri, MetadataSet metadata)
10: {
11: //創建Binding
12: TextMessageEncodingBindingElement messageEncodingElement = new TextMessageEncodingBindingElement() { MessageVersion = MessageVersion.None };
13: HttpTransportBindingElement transportElement = new HttpTransportBindingElement();
14: Utility.SetPropertyValue(transportElement, "Method", "GET");
15: Binding binding = new CustomBinding(messageEncodingElement, transportElement);
16:
17: //創建ChannelListener
18: IChannelListener listener = binding.BuildChannelListener<IReplyChannel>(listenUri, string.Empty, ListenUriMode.Explicit, new BindingParameterCollection());
19: ChannelDispatcher dispatcher = new ChannelDispatcher(listener, "ServiceMetadataBehaviorHttpGetBinding", binding) { MessageVersion = binding.MessageVersion };
20:
21: //創建EndpointDispatcher
22: EndpointDispatcher endpoint = new EndpointDispatcher(new EndpointAddress(listenUri), "IHttpGetMetadata", "http://www.artech.com/");
23:
24: //創建DispatchOperation,並設置DispatchMessageFormatter和OperationInvoker
25: DispatchOperation operation = new DispatchOperation(endpoint.DispatchRuntime, "Get", "*", "*");
26: operation.Formatter = Utility.CreateInstance<IDispatchMessageFormatter>(MessageOperationFormatterType, Type.EmptyTypes, new object[0]);
27: MethodInfo method = typeof(IHttpGetMetadata).GetMethod("Get");
28: operation.Invoker = Utility.CreateInstance<IOperationInvoker>(SyncMethodInvokerType, new Type[] { typeof(MethodInfo) }, new object[] { method });
29: endpoint.DispatchRuntime.Operations.Add(operation);
30:
31: //設置SingletonInstanceContext和InstanceContextProvider
32: MetadataProvisionService serviceInstance = new MetadataProvisionService(metadata);
33: endpoint.DispatchRuntime.SingletonInstanceContext = new InstanceContext(host, serviceInstance);
34: endpoint.DispatchRuntime.InstanceContextProvider = Utility.CreateInstance<IInstanceContextProvider>(SingletonInstanceContextProviderType, new Type[] { typeof(DispatchRuntime) }, new object[] { endpoint.DispatchRuntime });
35: dispatcher.Endpoints.Add(endpoint);
36:
37: //設置ContractFilter和AddressFilter
38: endpoint.ContractFilter = new MatchAllMessageFilter();
39: endpoint.AddressFilter = new MatchAllMessageFilter();
40:
41: host.ChannelDispatchers.Add(dispatcher);
42: }
43: }

大體上介紹一下創建ChannelDispatcher的邏輯。首先創建綁定對象,該綁定由兩個綁定元素構成:TextMessageEncodingBindingElement和HttpTransportBindingElement,這些因為元數據請求消息就是單純的HTTP-GET請求消息,並不是一個SOAP,所以需要將HttpTransportBindingElement的消息版本設為None,並將Method屬性(這是一個internal屬性)設為GET。

然後利用創建的綁定對象創建ChannelListener,並基於該ChannelListener創建ChannelDispatcher對象。當ChannelDispatcher成功創建,開始創建EndpointDispatcher對象,並定制該EndpointDispatcher的DispatchRuntime。這其中包括創建DispatchOperation對象以及相關的消息格式化器以及操作執行器。然後是我們熟悉的對InstanceContextProvider和SingletonInstanceContext的設定。最後需要設置EndpointDispatcher的兩個消息篩選器:契約篩選器和地址篩選器,在這將它們設置成MatchAllMessageFilter類型,使之能夠匹配所有的請求消息。關於WCF的消息篩選機制,在《WCF技術剖析(卷1)》第2章有詳細介紹。

待DispatchRuntime被成功定制,將創建的EndpointDispatcher添加到ChannelDispatcher的EndpointDispatcher列表,最終再將ChannelDispatcher添加到ServiceHost的ChannelDispatcher列表中。

然後,我們在ServiceMetadataBehaviorAttribute添加兩個屬性:HttpGetEnabled和HttpGetUrl,前者表示是否采用基於HTTP-GET的元數據發布模式,後者指定元數據發布的地址。並將上面定義的CreateHttpGetChannelDispatcher添加到ApplyDispatchBehavior方法中。

   1: [AttributeUsage(AttributeTargets.Class)]
2: public class ServiceMetadataBehaviorAttribute : Attribute, IServiceBehavior
3: {
4: //其他成員
5: public bool HttpGetEnabled
6: { get; set; }
7: public string HttpGetUrl
8: { get; set; }
9: public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
10: {
11: MetadataSet metadata = GetExportedMetadata(serviceDescription);
12: CustomizeMexEndpoints(serviceDescription, serviceHostBase, metadata);
13: if (this.HttpGetEnabled)
14: {
15: CreateHttpGetChannelDispatcher(serviceHostBase, new Uri(this.HttpGetUrl), metadata);
16: }
17: }
18: }

那麼現在我們就可以通過下面的方式將ServiceMetadataBehaviorAttribute應用到我們的CalculatorService,並通過HttpGetUrl屬性指定原數據發布的目標地址:

   1: [ServiceMetadataBehavior(HttpGetEnabled = true, HttpGetUrl = "http://127.0.0.1:9999/calculatorservice/mex")]
2: public class CalculatorService : ICalculator, IMetadataProvisionService
3: {
4: //省略成員
5: }

如果CalculatorService被成功寄宿,直接通過浏覽器訪問元數據發布的地址(http://127.0.0.1:9999/calculatorservice/mex),你可以看到與圖2一樣的結果。

圖2 通過IE獲取發布的元數據

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved