8. 元数据交换 (Metadata Exchange)
服务有两种方式来发布元数据。你可以通过 HTTP-GET 协议来提供元数据,或者使用专门的 MEX 端点。WCF 可以自动透过 HTTP-GET 提供元数据,你要做的全部工作仅仅是显式添加一个服务行为(service behavior) —— serviceMetadata,并将其属性 httpGetEnabled 设为 true。
<services>
<service behaviorConfiguration="Metadata" name="MyService">
<host>
<baseAddresses>
<add baseAddress="http://localhost:801/" />
</baseAddresses>
</host>
<endpoint binding="basicHttpBinding" contract="IMyService" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="Metadata">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
一 旦你允许通过 HTTP-GET 交换元数据,那么你可以在网页浏览器中输入基本地址(如果指定了)查看服务的相关信息。你会看到一个服务确认页,其中包含了服务的基本信息,以及如何创建 客户端代理的提示。这个确认页与 IIS hosting 无关,Self-hosting 同样可以看到。
我们同样可以在编码中指定这种行为。
host.AddServiceEndpoint(typeof(IMyService), new BasicHttpBinding(), "MyService");
ServiceMetadataBehavior metadataBehavior = host.Description.Behaviors.Find<ServiceMetadataBehavior>( );
if(metadataBehavior == null)
{
metadataBehavior = new ServiceMetadataBehavior( );
metadataBehavior.HttpGetEnabled = true;
host.Description.Behaviors.Add(metadataBehavior);
}
host.Open( );
ServiceMetadataBehavior 还有另外一个属性 HttpGetUrl,以便我们设置一个自定义元数据发布地址(绝对地址或相对地址)。
app.config / web.config
或
Metadata Exchange Endpoint
服务还可以通过一个名为 "metadata exchange endpoint" 的专用端点来发布元数据,这个端点通常被称之为 "MEX 端点"。MEX 是行业标准的元数据交换格式。
public interface IMetadataExchange
{
[OperationContract(...)]
Message Get(Message request);
//More members
}
MEX 标准的实现复杂而繁琐,幸好 WCF 已经提供了相关的实现,我们只需做些简单的设置即可。使用 MEX 端点并不需要我们打开 HTTP-GET 设置,当然也不会起冲突,通常两者会同时存在。下面的例子中,我们提供了三个不同传输协议的 MEX 端点。这些 MEX 端点与其它服务端点一样,可以使用绝对路径,或者基于基本地址的相对路径。
<host>
<baseAddresses>
<add baseAddress = "net.tcp://localhost:8001/"/>
<add baseAddress = "net.pipe://localhost/"/>
</baseAddresses>
</host>
<endpoint
address = "MEX"
binding = "mexTcpBinding"
contract = "IMetadataExchange"
/>
<endpoint
address = "MEX"
binding = "mexNamedPipeBinding"
contract = "IMetadataExchange"
/>
<endpoint
address = "http://localhost:8000/MEX"
binding = "mexHttpBinding"
contract = "IMetadataExchange"
/>
</service>
<behaviors>
<serviceBehaviors>
<behavior name = "MEX">
<serviceMetadata />
</behavior>
</serviceBehaviors>
</behaviors>
注 意: 包含 MEX 端点的服务必须关联到一个拥有 serviceMetadata 元素的服务行为设置,哪怕 serviceMetadata 内部设置为空也不能省略。因为有该元素,宿主才会向 Description.Behaviors 里添加 ServiceMetadataBehavior。
和操作其它服务端点一样,我们也可以通过代码来添加 MEX 端点。首先我们向 host 添加 ServiceMetadataBehavior,然后添加一个 MEX 端点。所不同的是,我们必须使用 CustomBinding 来构建该端点所需的绑定对象。
metadataBehavior = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (metadataBehavior == null)
{
metadataBehavior = new ServiceMetadataBehavior();
host.Description.Behaviors.Add(metadataBehavior);
}
BindingElement bindingElement = new HttpTransportBindingElement();
CustomBinding binding = new CustomBinding(bindingElement);
host.AddServiceEndpoint(typeof(IMetadataExchange), binding, "MEX");
--------以下是完整演示代码--------------
(1) 配置文件方式演示
app.config
<configuration>
<system.serviceModel>
<services>
<service name="Learn.CUI.MyService" behaviorConfiguration="MexBehavior">
<host>
<baseAddresses>
<add baseAddress="http://localhost:801/" />
</baseAddresses>
</host>
<endpoint binding="basicHttpBinding" contract="Learn.CUI.IMyService" />
<endpoint address="MEX" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MexBehavior">
<serviceMetadata />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Program.cs
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
namespace Learn.CUI
{
[ServiceContract]
public interface IMyService
{
[OperationContract]
void Test();
}
public class MyService : IMyService
{
public void Test()
{
}
}
class Program
{
static void Main(string[] args)
{
ServiceHost host = new ServiceHost(typeof(MyService));
host.Open();
Console.ReadKey(true);
}
}
}
(2) 代码方式演示
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
namespace Learn.CUI
{
[ServiceContract]
public interface IMyService
{
[OperationContract]
void Test();
}
public class MyService : IMyService
{
public void Test()
{
}
}
class Program
{
static void Main(string[] args)
{
Uri baseAddress = new Uri("http://localhost:801/");
ServiceHost host = new ServiceHost(typeof(MyService), baseAddress);
host.AddServiceEndpoint(typeof(IMyService), new BasicHttpBinding(), "");
ServiceMetadataBehavior metadataBehavior;
metadataBehavior = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (metadataBehavior == null)
{
metadataBehavior = new ServiceMetadataBehavior();
host.Description.Behaviors.Add(metadataBehavior);
}
BindingElement bindingElement = new HttpTransportBindingElement();
CustomBinding binding = new CustomBinding(bindingElement);
host.AddServiceEndpoint(typeof(IMetadataExchange), binding, "MEX");
host.Open();
Console.ReadKey(true);
}
}
}