原文: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. 【转】emulator: ERROR: Could not load OpenGLES emulation library: lib64OpenglRender.so

    [转]emulator: ERROR: Could not load OpenGLES emulation library: lib64OpenglRender.so ./emulator64-arm ...

  2. 远程唤醒、WOL、Magic_Packet

    背景:很多人熟悉远程桌面并经常地利用它所带来的方便,但是前提是服务器(远程电脑)必须是处于开机状态.对于机房里有专人管理的服务器,这点不成问题,但如果是放在家里的电脑,要让它7*24地开机似乎就不好办 ...

  3. 南阳师范学院ACM官方博客使用说明

    登录之后跳到如下页面: 点击博客进入如下页面: 这里每个人都有一个专栏,大家可以把自己写得博客放到自己的专栏下,同时也可以查看其他人写的博客,相互交流! 在发表博客的时候,选择个人分类中自己的专栏即可 ...

  4. [LeetCode]题解(python):021-Merge Two Sorted Lists

    题目来源: https://leetcode.com/problems/merge-two-sorted-lists/ 题意分析: 题目给出两个排好序的链表,将这两个链表整合成一个新的有序的链表. 题 ...

  5. 【转】Plotting texts as graphs with R and igraph

    原文转自:http://blog.ynada.com/303 I’ve plotted several word association graphs for this New York Times ...

  6. HNOI2016 网络

    题目 朴素算法 在线. 树链剖分套一个堆. 时间复杂度\(O(n (\log n)^3)\). 分治 朴素算法中,套一个堆是为了支持删除操作. 采用以下分治可以避免删除操作: 每次对时间\([l,r] ...

  7. notify()、notifyAll()和wait()

    看到一道面试题,写一个多线程程序,交替输出1.2.1.2…… 先写下程序: /** * Created by Andrew on 2015/10/28. */ public class OutputT ...

  8. Objective-C基础教程读书笔记(6)

    第6章 源文件组织 到目前为止,我们讨论过的所有项目都是把源代码统统放入main.m文件中.类的main()函数,@interface和@implementation部分都被塞入同一个文件里.这种结构 ...

  9. 11997 - K Smallest Sums(优先队列)

    11997 - K Smallest Sums You’re given k arrays, each array has k integers. There are kk ways to pick ...

  10. Ext JS学习第五天 我们所熟悉的javascript(四)

    此文用来记录学习笔记: •javascript之对象.面向对象 •可能对于高级语言你可能了解甚至精通OOP面向对象,那么对于javascript你又熟悉多少呢?我们一起来学习javascript面向对 ...