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

简介:

基于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获取发布的元数据


作者:蒋金楠
微信公众账号:大内老A
微博: www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号 蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关文章
|
测试技术 网络架构
【技术干货连载 一】业务经过WAF HTTP 400问题排查
教你如何排查和解决业务经过WAF 七层代理HTTP 400问题原因?
508 1
|
6月前
|
安全 网络安全 定位技术
网络通讯技术:HTTP POST协议用于发送本地压缩数据到服务器的方案。
总的来说,无论你是一名网络开发者,还是普通的IT工作人员,理解并掌握POST方法的运用是非常有价值的。它就像一艘快速,稳定,安全的大船,始终为我们在网络海洋中的冒险提供了可靠的支持。
220 22
|
6月前
|
JSON JavaScript 前端开发
gRPC技术中的gRPC到HTTP的转换
从上述内容,我们可以看到,gRPC到HTTP的转换并没有改变gRPC强大的性能和可扩展性。它只是让这些强大的功能能更好地适应不同的环境和需求,兼容了更广泛的网络设施。所以,技术总是在变化,并且始终在寻找更好的平衡点,以满足不断变化的业务需求。我们也应该持续学习,掌握这些新的变化,以便更好地解决实际问题。
186 11
|
7月前
|
存储 编解码 开发工具
Android平台毫秒级低延迟HTTP-FLV直播播放器技术探究与实现
本文详细探讨了在Android平台上实现HTTP-FLV播放器的过程。首先介绍了FLV格式的基础,包括文件头和标签结构。接着分析了HTTP-FLV传输原理,通过分块传输实现流畅播放。然后重点讲解了播放器的实现步骤,涵盖网络请求、数据解析、音视频解码与渲染,以及播放控制功能的设计。文章还讨论了性能优化和网络异常处理的方法,并总结了HTTP-FLV播放器的技术价值,尤其是在特定场景下的应用意义。
307 11
|
10月前
|
负载均衡 监控 安全
HTTP代理IP的安全与稳定技术与策略的结合
随着科技与互联网的发展,企业对代理的需求日益增长。为加强HTTP代理IP的安全性和稳定性,可采取用户教育、使用加密协议、定期更换IP、监控可用性、设置访问控制、负载均衡、配置防火墙及定期更新维护等措施。这些方法能有效提升代理服务的安全性和可靠性。
212 7
|
11月前
|
数据采集 负载均衡 大数据
HTTP代理IP技术的未来:从传统到创新
随着数字化时代的发展,网络安全、隐私保护及内容访问自由成为核心需求,短效动态HTTP代理IP凭借独特技术优势,展现出智能化、自动化、更高匿名性和安全性、多样化类型、高性能稳定性、合规性与道德标准、用户体验提升、市场竞争透明化及行业应用扩展等八大未来发展趋势。
188 1
|
移动开发 JavaScript 前端开发
"解锁axios GET请求新姿势!揭秘如何将数组参数华丽变身,让你的HTTP请求在云端翩翩起舞,挑战技术极限!"
【8月更文挑战第20天】二维码在移动应用中无处不在。本文详述了在UniApp H5项目中实现二维码生成与扫描的方法。通过对比插件`uni-app-qrcode`和库`qrcode-generator`生成二维码,以及使用插件和HTML5 API进行扫描,帮助开发者挑选最佳方案。无论是即插即用的插件还是灵活的JavaScript实现,都能满足不同需求。
111 0
|
JavaScript 前端开发
基于 Node.js 环境,使用内置 http 模块,创建 Web 服务程序
基于 Node.js 环境,使用内置 http 模块,创建 Web 服务程序
深度探讨 Golang 中并发发送 HTTP 请求的最佳技术
深度探讨 Golang 中并发发送 HTTP 请求的最佳技术
262 4
|
前端开发 API UED
AngularJS的$http服务:深入解析与进行HTTP请求的技术实践
【4月更文挑战第28天】AngularJS的$http服务是核心组件,用于发起HTTP请求与服务器通信。$http服务简化了通信过程,通过深入理解和实践,能构建高效、可靠的前端应用。

热门文章

最新文章