zoukankan      html  css  js  c++  java
  • WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于HTTPGET的实现](提供模拟程序)

    基于HTTP-GET的元数据发布方式与基于WS-MEX原理类似,但是ServiceMetadataBehavior需要做的更多额外的工作。原因很简单,由于在WS-MEX模式下,我们为寄宿的服务添加了相应的MEX终结点,那么当服务被成功寄宿后,WCF已经为元数据的消息交换建立了如图1所示的分发体系,我们需要做的仅仅是对MEX终结点的DispatchRuntime进行相应的定制而已。

    image 图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,最后对EndpointDispatcherDispatchRuntime进行定制。为此,我单独写了一个方法: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的逻辑。首先创建绑定对象,该绑定由两个绑定元素构成:TextMessageEncodingBindingElementHttpTransportBindingElement,这些因为元数据请求消息就是单纯的HTTP-GET请求消息,并不是一个SOAP,所以需要将HttpTransportBindingElement的消息版本设为None,并将Method属性(这是一个internal属性)设为GET。

    然后利用创建的绑定对象创建ChannelListener,并基于该ChannelListener创建ChannelDispatcher对象。当ChannelDispatcher成功创建,开始创建EndpointDispatcher对象,并定制该EndpointDispatcherDispatchRuntime。这其中包括创建DispatchOperation对象以及相关的消息格式化器以及操作执行器。然后是我们熟悉的对InstanceContextProvider和SingletonInstanceContext的设定。最后需要设置EndpointDispatcher的两个消息筛选器:契约筛选器地址筛选器,在这将它们设置成MatchAllMessageFilter类型,使之能够匹配所有的请求消息。关于WCF的消息筛选机制,在《WCF技术剖析(卷1)》第2章有详细介绍。

    DispatchRuntime被成功定制,将创建的EndpointDispatcher添加到ChannelDispatcherEndpointDispatcher列表,最终再将ChannelDispatcher添加到ServiceHost的ChannelDispatcher列表中。

    然后,我们在ServiceMetadataBehaviorAttribute添加两个属性:HttpGetEnabledHttpGetUrl,前者表示是否采用基于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一样的结果。 

    image

    图2 通过IE获取发布的元数据

    作者:Artech
    出处:http://artech.cnblogs.com
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    Poj 3177 Redundant Paths (双连通分支+节点统计)
    Uva 796 Critical Links (割边+排序)
    Lightoj 1020
    Flip Game---poj1753(状压+bfs)
    Best Cow Line---poj3617(贪心)
    滑雪---poj1088(动态规划+记忆化搜索)
    King's Quest---poj1904(连通图缩点)
    Steady Cow Assignment---poj3189(多重匹配+二分)
    B. Berland National Library---cf567B(set|模拟)
    HDU Today---hdu2112(最短路-_-坑在是无向图)
  • 原文地址:https://www.cnblogs.com/artech/p/1633461.html
Copyright © 2011-2022 走看看