原文: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,最后对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一样的结果。

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

作者:Artech
出处:http://artech.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于HTTP-GET的实现](提供模拟程序)的更多相关文章

  1. WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于WS-MEX的实现](提供模拟程序)

    原文:WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于WS-MEX的实现](提供模拟程序) 通过<如何将一个服务发布成WSDL[编程篇]>的介绍我们知道了如何可以通过编程或者配 ...

  2. WCF技术剖析之二十七: 如何将一个服务发布成WSDL[编程篇]

    原文:WCF技术剖析之二十七: 如何将一个服务发布成WSDL[编程篇] 对于WCF服务端元数据架构体系来说,通过MetadataExporter将服务的终结点导出成MetadataSet(参考< ...

  3. WCF技术剖析之二十四: ServiceDebugBehavior服务行为是如何实现异常的传播的?

    原文:WCF技术剖析之二十四: ServiceDebugBehavior服务行为是如何实现异常的传播的? 服务端只有抛出FaultException异常才能被正常地序列化成Fault消息,并实现向客户 ...

  4. WCF技术剖析之二十八:自己动手获取元数据[附源代码下载]

    原文:WCF技术剖析之二十八:自己动手获取元数据[附源代码下载] 元数据的发布方式决定了元数据的获取行为,WCF服务元数据架构体系通过ServiceMetadataBehavior实现了基于WS-ME ...

  5. WCF技术剖析之二十六:如何导出WCF服务的元数据(Metadata)[实现篇]

    原文:WCF技术剖析之二十六:如何导出WCF服务的元数据(Metadata)[实现篇] 元数据的导出就是实现从ServiceEndpoint对象向MetadataSet对象转换的过程,在WCF元数据框 ...

  6. WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[中篇]

    原文:WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[中篇] 在[上篇]中,我们分别站在消息交换和编程的角度介绍了SOAP Fault和FaultException异常.在服务执行过 ...

  7. WCF技术剖析之二十一:WCF基本异常处理模式[中篇]

    原文:WCF技术剖析之二十一:WCF基本异常处理模式[中篇] 通过WCF基本的异常处理模式[上篇], 我们知道了:在默认的情况下,服务端在执行某个服务操作时抛出的异常(在这里指非FaultExcept ...

  8. WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载]

    原文:WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载] 我们有两种典型的WCF调用方式:通过SvcUtil.exe(或者添加Web引用)导入发布的服务元数据生成服务代理相关的代码 ...

  9. WCF技术剖析之二十六:如何导出WCF服务的元数据(Metadata)[扩展篇]

    原文:WCF技术剖析之二十六:如何导出WCF服务的元数据(Metadata)[扩展篇] 通过<实现篇>对WSDL元素和终结点三要素的之间的匹配关系的介绍,我们知道了WSDL的Binding ...

随机推荐

  1. 最简单的javascript 竖向菜单

    <html xmlns="http://www.w3.org/1999/xhtml"><head runat="server">    ...

  2. Java多线程之synchronized(一)

    在上节中已经说过了“非线程安全”是如何出现的,链接如下:http://www.cnblogs.com/chentong/p/5650137.html,那么怎么解决“非线程安全”问题呢,只需要在两个线程 ...

  3. 使用jsonEditor打造一个复杂json编辑器

    最近研究一个web版的json编辑器,在github中搜索,发现了这个利器. https://github.com/jdorn/json-editor 几经研究,终于把该控件的大部分功能研究透彻. 发 ...

  4. WPF自学笔记

    WPF使用哪几种元素作为顶级元素: 1. Window元素 2. Page元素(与Window元素类似,用于可导航的应用程序) 3. Application元素(定义应用程序资源和启动设置) PS:在 ...

  5. JavaSE学习总结第12天_API常用对象2

      12.01 Scanner的概述和构造方法原理 Scanner类概述:JDK5以后用于获取用户的键盘输入 构造方法:public Scanner(InputStream source) publi ...

  6. BZOJ 3304: [Shoi2005]带限制的最长公共子序列( LCS )

    求个LCS, 只是有了限制, 多加一维表示匹配到z串的第几个, 然后用滚动数组 ------------------------------------------------------------ ...

  7. 类似jquery的一个demo

    通过以下的demo,可以大体知道jquery的一些组织结构以及一些实现方法. 实际上jquery就是一个全局变量,只是在这个变量上添加了各种属性和方法. 首先我们要理解什么是匿名函数自执行,简单点就是 ...

  8. linux安装LNMP的资源

    wget http://nginx.org/download/nginx-0.7.69.tar.gz wget http://museum.php.net/php5/php-5.2.10.tar.gz ...

  9. codeforces 629D. Babaei and Birthday Cake

    题目链接 大意就是给出一个序列, 然后让你从中找出一个严格递增的数列, 使得这一数列里的值加起来最大. 用线段树, 先将数列里的值离散,然后就是线段树单点更新, 区间查询最值. 具体看代码. #inc ...

  10. WebRTC–getUserMedia-filter

    示例说明:抓取MediaStream的一帧数据,并对该帧数据使用Css滤镜效果. 步骤: 1. 由getUserMedia方法获取一个可用的MediaStream 2. canvas方法drawIma ...