zoukankan      html  css  js  c++  java
  • 【WCF】自定义地址头的筛选器

    前面的文章中,老周已向大伙伴们介绍了如何在终结点上使用地址头,只要服务是沿着该终结点调用的,那么每一次调用都会自动把地址头插入到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() == 0)
                {
                    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类,这只是为了让其支持配置文件。

    本文示例代码下载地址

  • 相关阅读:
    postgis 利用 php 返回geojson格式数据
    openlayers 3读取加载geojson格式数据
    openlayers 3加载百度、高德、google瓦片地图
    ol2 和 bootstrap样式冲突的问题
    Openlayers 2 取消鼠标缩放地图的功能
    Struts2之2.5.10配置
    ol3修改右下键的Attribution
    openlayers 2 高亮显示元素以及通过属性查询高亮某一元素
    sql查看锁与解锁
    使用jQuery解析JSON数据
  • 原文地址:https://www.cnblogs.com/tcjiaan/p/5757950.html
Copyright © 2011-2022 走看看