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获取发布的元数据
出处:http://artech.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
转载于:https://www.cnblogs.com/lonelyxmas/p/4635861.html
WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于HTTP-GET的实现](提供模拟程序)...相关推荐
- WCF技术剖析之二十八:自己动手获取元数据[附源代码下载]
WCF技术剖析之二十八:自己动手获取元数据[附源代码下载] 原文:WCF技术剖析之二十八:自己动手获取元数据[附源代码下载] 元数据的发布方式决定了元数据的获取行为,WCF服务元数据架构体系通过Ser ...
- WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载]
原文:WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载] 我们有两种典型的WCF调用方式:通过SvcUtil.exe(或者添加Web引用)导入发布的服务元数据生成服务代理相关的代码 ...
- WCF技术剖析之二十五: 元数据(Metadata)架构体系全景展现[WS标准篇]
元数据实际上是服务终结点的描述,终结点由地址(Address).绑定(Binding)和契约(Contract)经典的ABC三要素组成.认真阅读过<WCF技术剖析(卷1)>的读者相对会对这 ...
- WCF技术剖析之二十一:WCF基本异常处理模式[中篇]
通过WCF基本的异常处理模式[上篇], 我们知道了:在默认的情况下,服务端在执行某个服务操作时抛出的异常(在这里指非FaultException异常),其相关的错误信息仅仅限于服务端可见,并不会被WC ...
- WCF技术剖析之二十: 服务在WCF体系中是如何被描述的?
任何一个程序都需要运行于一个确定的进程中,进程是一个容器,其中包含程序实例运行所需的资源.同理,一个WCF服务的监听与执行同样需要通过一个进程来承载.我们将为WCF服务创建或指定一个进程的方式称为服务 ...
- [原创]WCF技术剖析之二十: 服务在WCF体系中是如何被描述的?
任何一个程序都需要运行于一个确定的进程中,进程是一个容器,其中包含程序实例运行所需的资源.同理,一个WCF服务的监听与执行同样需要通过一个进程来承载.我们将为WCF服务创建或指定一个进程的方式称为服务 ...
- WCF技术剖析之二十三:服务实例(Service Instance)生命周期如何控制[上篇](转)...
http://www.cnblogs.com/artech/archive/2009/11/05/1596925.html 服务调用的目的体现在对某项服务功能的消费上,而功能的实现又定义在相应的服务类 ...
- WCF技术剖析之二十六:如何导出WCF服务的元数据(Metadata)[实现篇]
元数据的导出就是实现从ServiceEndpoint对象向MetadataSet对象转换的过程,在WCF元数据框架体系中,元数据的导出工作由MetadataExporter实现.MetadataExp ...
- WCF技术剖析之二十三:服务实例(Service Instance)生命周期如何控制[中篇]
在[第1篇]中,我们介绍了WCF关于实例管理一些基本的知识点,包括InstanceContext.InstanceContextMode.已经如何通过ServiceBehaviorAttribute应 ...
最新文章
- Python零基础入门(1)——Linux下编程图形化界面的安装
- js双等号探索(一): [] == ![]为Ture ?
- 安全研究 | Jenkins 任意文件读取漏洞分析
- zynq交叉编译环境
- pymysql 写入数据
- QString和string互相转换乱码处理
- 【小白学PyTorch】 2.浅谈训练集验证集和测试集
- Google小组研发模式分析 1
- 【编译原理】学习LUA
- Servlet的学习之Request请求对象(2)
- hdu 1162(最小生成树kruskal)
- 【TJOI2016】【bzoj4552】排序(二分答案+线段树01排序)
- 国内Linux各版本占有率,Linux市场占有率知多少?
- PHP公众号商家收银台微信扫码付款支付源码
- 陶大程招收博士计算机视觉,回顾优必选AI首席科学家陶大程博士获IEEE ICDM研究贡献奖的相关事件...
- Kali 开机报错解决方案
- linux 系统迁移到固态硬盘,Linux系统迁移记录(从HDD到SSD)
- 华硕天选3和神舟战神Z8那个好?
- BGP SOO 防环机制
- 黑马程序员 一、java 概述与基础知识
热门文章
- python字典与json转换_python字典与json转换的方法总结
- python不能创建字典的是_用Python创建带有重复键的字典
- linux怎么进入文件夹_Linux基础命令《上》
- tkinter 菜单添加事件_Tasker的最新测试劫持了Android 11的电源菜单
- 硬币问题——固定终点的最长路和最短路
- 160 - 47 DueList.2
- 《MySQL——外部检测与内部统计 判断 主库是否出现问题》
- puppeteer api_使用Node.js和Puppeteer API生成PDF文件
- BX、DI、SI、BP总结
- 程序猿不能错过的十部电影