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

对于WCF服务端元数据架构体系来说,通过MetadataExporter将服务的终结点导出成MetadataSet(参考《如何导出WCF服务的元数据》),仅仅是完成了一半的工作。被成功导出的以MetadataSet对象表示的元数据需要最终作为可被访问的网络资源发布出来,才能被服务消费者获取,进而有效地帮助他们进行服务调用。元数据的发布最终是通过ServiceMetadataBehavior这样一个服务行为实现的,我们先来认识一下ServiceMetadataBehavior

一、 元数据发布的实现者:ServiceMetadataBehavior

ServiceMetadataBehavior是一个实现了IServiceBehavior的服务行为,它实现了基于如下两种协议的元数据发布模式:

  • HTTP-GET:采用HTTP协议的Get操作,向元数据目标地址发送HTTP请求,并以查询字符串(QueryString)的形式表示相应的查询参数。元数据最终以HTTP回复的形式返回;
  • WS-MEX:元数据提供者按照WS-MEX规范创建终结点发布元数据,元数据消费者创建同样基于WS-MEX的终结点与之交互,并最终通过SOAP的形式获取元数据。关于WS-MEX,可以参考我的文章《元数据(Metadata)架构体系全景展现[WS标准篇]

我们首先通过如下得代码来看看ServiceMetadataBehavior的定义,ServiceMetadataBehavior实现IServiceBehavior接口,并将所有发布元数据的行为定义在ApplyDispatchBehavior方法中。

   1: public class ServiceMetadataBehavior : IServiceBehavior
   2: {
   3:    
   4:     //其他成员
   5:     void IServiceBehavior.AddBindingParameters(ServiceDescription description, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection parameters);
   6:     void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase);
   7:     void IServiceBehavior.Validate(ServiceDescription description, ServiceHostBase serviceHostBase);
   8:  
   9:     public Uri ExternalMetadataLocation { get; set; }    
  10:  
  11:     //HTTP
  12:     public bool HttpGetEnabled { get; set; }
  13:     public Uri HttpGetUrl { get; set; }
  14:     public Binding HttpGetBinding { get; set; }
  15:  
  16:     //HTTPS
  17:     public bool HttpsGetEnabled { get; set; }
  18:     public Binding HttpsGetBinding { get; set; }
  19:     public Uri HttpsGetUrl { get; set; }
  20:  
  21:     public MetadataExporter MetadataExporter { get; set; }
  22: }

ServiceMetadataBehavior定义了一系列的属性用于控制具体的元数据发布行为,其中绝大部分是基于HTTP-GET发布模式的。ExternalMetadataLocation表示返回给客户端的一个外部元数据地址,可以是绝对地址,也可以是基于HttpGetUrl或者HttpsGetUrl表述的相对地址;基于HTTP-GET的元数据发布同时支持HTTP和HTTPS两种形式,Http(s)GetEnabled是控制是否允许基于HTTP(s)进行元数据发布的开关,Http(s)GetUrl和Http(s)GetBinding这指定了采用的地址和绑定;MetadataExporter属性表示的MetadataExporter对象用于进行元数据的导出,默认为WsdlExporter

你可以通过配置的方式来设置除MetadataExporter之外的所有ServiceMetadataBehavior的属性,此外,WCF还提供给你一些额外的配型项供你更好地控制元数据的发布行为。对于WCF的开发者或者实施者来说,当你没有一份完备的文档指导你进行基于服务行为或者终结点行为的配置时,你可以查看该行为对应的BehaviorExtensionElement的定义获取与该行为相关的所有配置信息。ServiceMetadataBehavior相关的配置项全部定义在ServiceMetadataPublishingElement中,下面给出了ServiceMetadataPublishingElement的定义:

   1: public sealed class ServiceMetadataPublishingElement : BehaviorExtensionElement
   2: {
   3:     //其他成员
   4:     public override Type BehaviorType { get; }
   5:     [ConfigurationProperty("externalMetadataLocation")]
   6:     public Uri ExternalMetadataLocation { get; set; }
   7:  
   8:     //HTTP
   9:     [ConfigurationProperty("httpGetEnabled", DefaultValue = false)]
  10:     public bool HttpGetEnabled { get; set; }
  11:     [ConfigurationProperty("httpGetUrl")]
  12:     public Uri HttpGetUrl { get; set; }
  13:     [StringValidator(MinLength=0), ConfigurationProperty("httpGetBinding", DefaultValue="")]
  14:     public string HttpGetBinding { get; set; }
  15:     [ConfigurationProperty("httpGetBindingConfiguration", DefaultValue=""), StringValidator(MinLength=0)]
  16:     public string HttpGetBindingConfiguration { get; set; }
  17:  
  18:     //HTTPS
  19:     [ConfigurationProperty("httpsGetEnabled", DefaultValue = false)]
  20:     public bool HttpsGetEnabled { get; set; }
  21:     [ConfigurationProperty("httpsGetUrl")]
  22:     public Uri HttpsGetUrl { get; set; }
  23:     [StringValidator(MinLength=0), ConfigurationProperty("httpsGetBinding", DefaultValue="")]
  24:     public string HttpsGetBinding { get; set; }
  25:     [StringValidator(MinLength=0), ConfigurationProperty("httpsGetBindingConfiguration", DefaultValue="")]
  26:     public string HttpsGetBindingConfiguration { get; set; }
  27:  
  28:     [ConfigurationProperty("policyVersion", DefaultValue="Default"), TypeConverter(typeof(PolicyVersionConverter))]
  29:     public PolicyVersion PolicyVersion { get; set; }
  30: }

通过对ServiceMetadataPublishingElement的定义我们可以看出:我们可以通过除MetadataExporter之外的所有ServiceMetadataBehavior的属性,还可以通过policyVersion配置向指定具体采用的WS-Policy的版本。下面是一个ServiceMetadataBehavior配置的例子:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:     <system.serviceModel>
   4:         <services>
   5:             <service behaviorConfiguration="MetadataPublishBehavior" name=" Artech.Services.CalculatorService">
   6:                 <endpoint address="http://127.0.0.1:3721/calcuulatorservice"
   7:                     binding="basicHttpBinding" bindingConfiguration="" contract=" Artech.Contracts.ICalculator" />                
   8:             </service>
   9:         </services>
  10:  
  11:       <behaviors>
  12:         <serviceBehaviors>
  13:           <behavior name="MetadataPublishBehavior">
  14:             <serviceMetadata externalMetadataLocation="http://127.0.1/mex/calculatorservice"
  15:                 httpGetEnabled="true" httpGetUrl="http://127.0.0.1/calculatorservice/mex"
  16:                 httpsGetEnabled="true" httpsGetUrl="https://127.0.0.1/calculatorservice/mex"
  17:                 policyVersion="Policy15" />
  18:           </behavior>
  19:         </serviceBehaviors>
  20:       </behaviors>
  21: </system.serviceModel>
  22: </configuration>

如果你希望通过WS-MEX的方式进行元数据的发布,你需要为服务添加一个基于WS-MEX的终结点。基于WS-MEX的终结点和一般意义上的终结点一样由地址、绑定和契约三部分组成。其中,地址表示发布元数据的目标地址,而绑定和契约因为需要按照WS-MEX规范进行消息的交换,所以对绑定和契约具有特殊的要求。在具体对MEX终结点展开介绍之前,我们不妨先来看看如何通过配置的方式为服务添加MEX终结点:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:     <system.serviceModel>
   4:         <services>
   5:             <service name="Artech.Services.CalculatorService">
   6:                 <endpoint address="http://127.0.0.1:3721/calcuulatorservice"
   7:                     binding="basicHttpBinding" bindingConfiguration="" contract="Artech.Contracts.ICalculator" />
   8:                 <endpoint address="http://127.0.0.1:3721/calcuulatorservice/mex"
   9:                     binding="mexHttpBinding" bindingConfiguration="" contract="IMetadataExchange" />               
  10:             </service>
  11:         </services> 
  12:     </system.serviceModel>
  13: </configuration>

二、MEX 终结点有何不同?

我们通过为服务添加基于WS-MEX的终结点(以下简称MEX终结点)实现支持WS-MEX的元数据发布方式。总的来说,MEX终结点和一般意思上的终结点并没有本质的不同,也是由地址、绑定和契约三要素构成。但是,为了支持WS-MEX规定的消息交换模式和请求/回复消息的结构,对契约和绑定具有一些特殊的要求,先来看看MEX终结点的契约。

1、MEX终结点的契约:IMetadataExchange

从上面给出的基于MEX终结点的配置中,我们可以看到该终结点的契约被配置成“IMetadataExchange”。实际上IMetadataExchange是WCF内部定义的一个特殊服务契约接口,定义在System.ServiceModel.Description命名空间下,下面是IMetadataExchange的定义:

   1: [ServiceContract(ConfigurationName = "IMetadataExchange", Name = "IMetadataExchange", Namespace = "http://schemas.microsoft.com/2006/04/mex")]
   2: public interface IMetadataExchange
   3: {
   4:      [OperationContract(Action = "http://schemas.xmlsoap.org/ws/2004/09/transfer/Get", ReplyAction = "http://schemas.xmlsoap.org/ws/2004/09/transfer/GetResponse", AsyncPattern = true)]
   5:     IAsyncResult BeginGet(Message request, AsyncCallback callback, object state);
   6:     Message EndGet(IAsyncResult result);
   7:     [OperationContract(Action = "http://schemas.xmlsoap.org/ws/2004/09/transfer/Get", ReplyAction = "http://schemas.xmlsoap.org/ws/2004/09/transfer/GetResponse")]
   8:     Message Get(Message request);
   9: }

从定义可以看出,IMetadataExchange实际上仅仅包含一个Get服务操作,其中Get方法是正常的同步模式服务操作,而BeginGet/EndGet是按照标准的异步操作模式对Get服务操作的定义(关于异步服务操作模式,在《WCF技术剖析(卷1)》的第4章有详细的介绍)。

进一步分析IMetadataExchange的定义,由于通过ServiceContractAttribute特性将ConfigurationName属性设定成“IMetadataExchange”,这也是为何在进行MEX终结点的配置的时候,契约可以直接配置成“IMetadataExchange”,而不是“System.ServiceModel.Description. IMetadataExchange”。

再来看看Get操作,通过OperationContractAttribute特性将Action和ReplyAction设置成了http://schemas.xmlsoap.org/ws/2004/09/transfer/Gethttp://schemas.xmlsoap.org/ws/2004/09/transfer/GetResponse,如果读者《元数据(Metadata)架构体系全景展现[WS标准篇]》WS-Transfer相关介绍还有印象的话,应该知道它们就是WS-Transfer Get请求和回复SOAP消息对应的Action的值。在介绍WS—MEX的时候,我们提到过WS-MEX支持两种形式的元数据获取方式:WS-Transfer Get操作请求和Get Metadata操作请求。从这里可以看出,WCF采用的是基于WS-Transfer Get操作的元数据请求方式。

2、MEX终结点的绑定:MetadataExchangeBindings

WCF专门为MEX终结点定制了一系列的绑定,以实现对不同的网络传输协议(HTTP、HTTPS、TCP或者Named Pipe)的支持。这些定制的MEX绑定定义在MetadataExchangeBindings静态类中,你可以通过相应CreateMexXxxBinding方法创建基于某种传输协议的绑定。MetadataExchangeBindings的定义如下:

   1: public static class MetadataExchangeBindings
   2: {
   3:     //其他成员
   4:     public static Binding CreateMexHttpBinding();
   5:     public static Binding CreateMexHttpsBinding();
   6:     public static Binding CreateMexTcpBinding();   
   7:     public static Binding CreateMexNamedPipeBinding(); 
   8: }

如果你采用编程的方式为服务添加MEX终结点,那么你可以直接借助MetadataExchangeBindings创建相应的MEX绑定。如果采用配置的方式,仅仅需要将终结点的binding配置成:mexHttpBinding、mexHttpsBinding、mexTcpBinding和mexNamedPipeBinding即可,具体配置可以参考下面:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:     <system.serviceModel>
   4:         <services>
   5:             <service behaviorConfiguration="MetadataPublishBehavior" name=" Artech.Services.CalculatorService">
   6:                 <endpoint address="http://127.0.0.1:3721/calcuulatorservice"
   7:                     binding="basicHttpBinding" bindingConfiguration="" contract="Artech.Contracts.ICalculator" />                
   8:                 <endpoint address="http://127.0.0.1/calculatorservice/mex" binding="mexHttpBinding" contract="IMetadataExchange" />
   9:                 <endpoint address="https://127.0.0.1/calculatorservice/mex" binding="mexHttpsBinding" contract="IMetadataExchange" />
  10:                 <endpoint address="net.tcp://127.0.0.1/calculatorservice/mex"                    binding="mexTcpBinding" contract="IMetadataExchange" />
  11:                 <endpoint address="net.pipe://127.0.0.1/calculatorservice/mex"                    binding="mexNamedPipeBinding" contract="IMetadataExchange" />
  12:             </service>
  13:         </services>
  14:     </system.serviceModel>
  15: </configuration>

下一篇中,我们将会讨论ServiceMetadataBehavior在内部是如何实现基于HTTP-GET和WS-MEX两种协议的元数据发布的。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏李鹏的专栏

Dubbo 发布恢复维护后的第一个版本 2.5.4

Dubbo 发布了恢复维护后的第一个版本 2.5.4,主要是解决 issues 和依赖升级。

16310
来自专栏Java技术栈

Redis PK Memcached,哪个更牛叉?

说到 redis 就会联想到 memcached,反之亦然。了解过两者的同学有那么个大致的印象:

9220
来自专栏张善友的专栏

分布式文件存储的数据库开源项目MongoDB

MongoDB是一个基于分布式文件存储的数据库开源项目。由C++语言编写。旨在为WEB应用提供可护展的高性能数据存储解决方案。 它的特点是高性能、易部署、易使用...

39190
来自专栏Vamei实验室

被解放的姜戈06 假作真时

之前了解了: 创建Django项目 数据库 模板 表格提交 admin管理页面 上面的功能模块允许我们做出一个具有互动性的站点,但无法验证用户的身份。我们这次了...

21860
来自专栏Danny的专栏

【SSH快速进阶】——探索Hibernate对象的三种状态:Transient、Persistent、Detached

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huyuyang6688/article/...

12020
来自专栏用户2442861的专栏

操作系统八内存管理

      CPU可以在一个cpu时钟内执行一个或多个其内置寄存器的指令。而访问内存需多个cpu时钟。由于内存频繁访问,可以再cpu与内存之间增加高速缓存

11010
来自专栏技术之路

go微服务框架go-micro深度学习(一) 整体架构介绍

      产品嘴里的一个小项目,从立项到开发上线,随着时间和需求的不断激增,会越来越复杂,变成一个大项目,如果前期项目架构没设计的不好,代码会越来越臃肿,难以...

2.3K30
来自专栏深度学习与计算机视觉

Python 上下文管理器

TensorFlow的运行模型—session(会话),用来执行定义好的运算,会话拥有并管理TensorFlow程序运行时的所有资源,所以当运算结束后需要对资源...

21390
来自专栏AndroidTv

讲讲断点续传那点儿事提问理论基础代码示例

这次想来讲讲断点续传,以前没相关需求,所以一直没去接触,近阶段了解了之后,其实并不复杂,那么也便来写一篇记录一下,分享给大伙,也方便自己后续查阅。

12220
来自专栏文武兼修ing——机器学习与IC设计

流水线式p2p接口的分析与实现

P2P接口是一种双向握手接口,传输的前级和后级各提供一个数据有效信号valid和忙信号busy信号,只有当两个信号达成某种指定情况时,握手完成,数据传输完成,否...

12120

扫码关注云+社区

领取腾讯云代金券