WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于HTTP-GET的实现](提供模拟程序)...
基于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獲取發布的元數據
作者:Artech出處:http://artech.cnblogs.com
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。 posted on 2015-07-10 15:25 NET未來之路 閱讀(...) 評論(...) 編輯 收藏
轉載于:https://www.cnblogs.com/lonelyxmas/p/4635861.html
總結
以上是生活随笔為你收集整理的WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于HTTP-GET的实现](提供模拟程序)...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 定义jQuery插件
- 下一篇: nano使用说明