前面的文章中,老周已向大伙伴们介绍了如何在终结点上使用地址头,只要服务是沿着该终结点调用的,那么每一次调用都会自动把地址头插入到SOAP消息的Header列表中。
而通过前一篇文章中的示例,大家也看到,客户端在调用服务时,必须指定与服务器完全一致的地址头,否则会验证失败。那是因为,在默认情况下,AddressFilter会对终结点的地址以及地址头进行校验。如果我们不希望使用默认校验行为,可以自定义一个MessageFiler,然后对传入的SOAP消息头进行验证。
MessageFilter是一个抽象类,它的结构如下:
public abstract class MessageFilter
{
……
public abstract bool Match(MessageBuffer buffer); public abstract bool Match(Message message);
……
}
我们重点要实现Match方法,如果校验成功,则返回true,如果不通过就返回false。Match方法有两个重载,我们核心要做的是处理参数类型为Message的版本,而参数类型为MessageBuffer的版本,只需要从buffer中读出一条SOAP消息,并传递给bool Match(Message message)方法即可。
下面代码演示该处理。
public override bool Match(MessageBuffer buffer)
{
Message msg = buffer.CreateMessage();
bool b= Match(msg);
msg.Close();
return b;
}
CreateMessage方法从字节缓冲区生成一条消息实例,然后调用另一个Match方法,并得到验证结果,最后把结果返回即可。由于此处的Message是从buffer产生的临时消息实例,因此用完后,可以调用Close方法释放掉。
接下来,我们重点实现参数类型为Message的Match方法重载。本例子我主要验证客户端的地址头中是否包含一个名为vip的XML元素,命名空间为member-vip,并且,XML元素内有一个名为star的attribute。假设它表示VIP会员的星级,比如一个购书服务程序,不同星级的VIP可以获得不折扣的优惠。
即客户端在调用时应提供这样的地址头:
<vip xmlns="member-vip" star="2" />
咱们这个自定义MessageFilter的任务是检查消息头中是否存在vip元素,且命名空间为member-vip,包含star特性。
public override bool Match(Message message)
{
var hd = message.Headers.FirstOrDefault(h => h.Namespace == HEADER_NS && h.Name == HEADER_ELNAME);
if (hd == null) return false; XElement ele = message.Headers.GetHeader<XElement>(HEADER_ELNAME, HEADER_NS); if (ele.Attributes("star").Count() == )
{
return false;
}
return true;
}
筛选器是在消息调度阶段执行的,负责对终结点进行调度的是EndpointDispatcher类,它有一个AddressFilter属性,引用的类型正是MessageFilter的派生类。故,我们只要把自定义的消息筛选器实例赋给AddressFilter属性即可,那么,如何赋值呢?
WCF为每个服务部分都提供了Behavior,不同的Behavior用于扩展不同的对象。比如,对服务本身,可以用Service Behavior来扩展;对于终结点,可以用Endpoint Behavior来扩展;对于服务协定,可以用Contract Behavior来扩展,等等。Behavior可以对各个对象的功能进行扩充,但在扩展时应当注意,扩展点最好与behavior相对应,即,如果扩展点是扩展终结点的行为的,就应该用Endpoint Behavior来扩展,而不要用Cannel Behavior来扩展。
AddressFilter是作用在终结点上的,所以,在扩展时应该实现IEndpointBehavior接口。不管是哪一种类型的behavior,通常我们在实现时,会实现以下两个方法:
ApplyDispatchBehavior——指的是behavior在服务器上被应用后的处理。
ApplyClientBehavior——指在客户端应用behavior后的处理。
AddressFilter只需要在服务器端进行处理,而不必考虑客户端,所以,重点实现ApplyDispatchBehavior方法即可。
public class MyEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{ } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{ } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.AddressFilter = new MyEndpointAddrFilter();
} public void Validate(ServiceEndpoint endpoint)
{ }
}
很简单,直接把自定义的MessageFilter实例赋值给AddressFilter属性就可以了。Validate方法是用来验证当前的终结点是否合法,本例中不用进行检验,如果你验证的话可以写上相应的代码,如果终结点不合法,直接抛出异常就行了。
随后,把这个自定义的终结点behavior插入到服务的终结点中即可。
using (ServiceHost host=new ServiceHost(typeof(SV)))
{
foreach (var svep in host.Description.Endpoints)
{
if (svep.EndpointBehaviors.Contains(typeof(MyEndpointBehavior)) == false)
{
svep.EndpointBehaviors.Add(new MyEndpointBehavior());
}
} host.Open(); ………………
现在自定义的地址头筛选器已经起作用了。
目前这个Behavior不支持配置文件,只能使用代码来插入到ServiceEndpoint中。
要是希望该behavior可以通过配置文件使用,可以实现BehaviorExtensionElement抽象类。代码如下:
public sealed class CustEndpointBehaviorElement : BehaviorExtensionElement
{
public override Type BehaviorType
{
get
{
return typeof(MyEndpointBehavior);
}
} protected override object CreateBehavior()
{
return new MyEndpointBehavior();
}
}
BehaviorType属性返回自定义behavior类型的Type,此处是MyEndpointBehavior类的Type。CreateBehavior方法返回自定义behavior的实例,此处当然是MyEndpointBehavior的实例了。
打开配置文件,在system.serviceModel节点下添加以下扩展声明:
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="CustomEndpointBehavior" type="TestApp.CustEndpointBehaviorElement,TestApp"/>
</behaviorExtensions>
</extensions>
</system.serviceModel>
name指定的是随后在配置文件中使用该扩展时的元素名称,type指定类型,类名要包含命名空间,逗号后面是程序集名称。
现在可以在behaviors节点下声明了。
<behaviors>
<endpointBehaviors>
<behavior name="svepbhv">
<CustomEndpointBehavior />
</behavior>
</endpointBehaviors>
</behaviors>
behavior的节点名称就是刚才在behaviorExtensions / add 下指定的name值。
最后,记得在endpoint节点上引用behavior配置。
<endpoint address="http://localhost:2288/demo" …… behaviorConfiguration="svepbhv"/>
好了,如此一来,自定义的地址头筛选方案就完成了。
看起来像是复杂一些,其实也没什么,总结起来就是:扩MessageFilter --> 扩Behavior --> 应用behavior,另外附加的就是实现BehaviorExtensionElement类,这只是为了让其支持配置文件。