上篇回顾

    上一篇,简单的讲了一下WCF里面如何拦截一个操作,不过,只说了一半,因为程序拦截住WCF的一个操作请求之后,并不知道应该去干什么,因此,这个拦截没有发挥它该有的功能。那么怎么才能让我们的拦截发挥作用,并且有能够实现主题——基于配置的实现哪?

    思考这个问题的时候,是否想到了在Web上大显身手的MVC模式哪?想一下MVC是如何判断应该调用那个Controller的,当然当然是RouteTable。

    那么我们为什么不也作一个路由表哪?

打造路由表——寻找Url的等价物

    开始打造我们自己的路由表之前,需要想明白一件事情,WCF里面的路由的URL怎么取,事实上,WCF的每个操作都有自己的Action,因此,完全可以用Action来作为WCF中Url的等价物。

    那么,怎么在WCF中抓取出当前操作的Action哪?

    其实,相当的简单,只需要这样一句就可以了:

string action = OperationContext.Current.IncomingMessageHeaders.Action;

    有了Action,这个Url的等价物之后,我们需要做的就是决定如何定义一个路由表,这个字符串简单,变成一个可以执行的操作。

打造路由表——思考路由策略

    因为是自己打造路由,所以,很多时候可以跳出现有的MVC之类的框架的思维定式,寻找一个真正想要的策略(即使不是什么最佳策略,也无关紧要)。

    对于任何一个WCF的操作来说,总是可以想象到下面这几步的:

  • 日志
  • 验证参数
  • 错误处理
  • 各种未知的操作

    其中,各种未知操作可能也会出现管道/过滤器式(pipe/filter)的处理,前一步做完再做下一步。因此,我们的路由表最好能支持链式处理。

    说到这里,是不是想到了什么?

    至少我在第一时间想到的是:设计模式——责任链模式。

打造路由表——基于责任链的路由表

    既然确定了要基于责任链,那么首先来准备责任链的基础类型:

public interface IOperationHandler
{
   IOperationHandler Next { get; set; }
   object Request { get; set; }
   object Response { get; set; }
   void Handle();
}

    此时,是不是发现上一篇中之前要限定操作只有一个参数和一个返回值了,以及只支持同步方法,为这里的接口带来了巨大的简化。

打造路由表——路由到原始处理

    在打造别的Handler之前,先写一个原始的WCF的处理

    internal sealed class RawHandler
        : IOperationHandler
    {

        public object Instance { get; private set; }
        public object Request { get; set; }
        public object Response { get; set; }
        private readonly IOperationInvoker m_raw;

        public RawHandler(object instance, IOperationInvoker raw)
        {
            Instance = instance;
            m_raw = raw;
        }

        public override void Handle()
        {
            object[] outputs;
            Response = m_raw.Invoke(Instance, new object[] { Request }, out outputs);
        }

    }

    有了这个RawHandler,就可以打造出:

    public abstract class HandlerFactory
    {

        private static HandlerFactory m_default = new DefaultHandlerFactory();
        public static HandlerFactory Default { get { return m_default; } }
        private static HandlerFactory m_current = m_default;
        public static HandlerFactory Current
        {
            get { return m_current; }
            set { m_current = value; }
        }

        public abstract IOperationHandler CreateHandlerChains(RawHandler raw);

    }

    internal sealed class DefaultHandlerFactory
        : HandlerFactory
    {

        public override IOperationHandler CreateHandlerChains(RawHandler raw)
        {
            IOperationHandler handler = raw;
            // todo : create handler chains ...
            return handler;
        }

    }

    然后,在把这个HandlerFactory放到上一篇的ControllerInvoker类里面:

        public object Invoke(object instance, object[] inputs, out object[] outputs)
        {
            var raw = new RawHandler(instance, Inner);
            var c = HandlerFactory.Current.CreateHandlerChains(raw);
            c.Request = inputs[0];
            c.Handle();
            outputs = new object[0];
            return c.Response;
        }

    到目前为止,WCF服务就有可以正常的跑起来了,当然目前没有更改任何WCF的行为,而仅仅是绕了一圈,把服务里面加了个拦截和路由,并且路由总是直接调用服务原来的代码实现。

    感到很沮丧?别急,虽然到这里写了一大堆类,并且没有更改任何的WCF的默认行为,但是,已经成功的加入了一个可以更改WCF行为的注入点:CreateHandlerChains方法

    通过这个方法,我们可以随时添加Handler,来修改WCF的行为。

打造路由表——硬编码的路由表

    在思考如何动态配置之前,不妨先从硬编码开始。所以写一个简单的HardCodeHandlerFactory:

    public class HardCodeHandlerFactory
        : HandlerFactory
    {

        private readonly List<HandlerInfo> Creators = new List<HandlerInfo>();

        private sealed class HandlerInfo
        {
            public string ActionPattern;
            public Func<IOperationHandler, IOperationHandler> Creator;

            public bool IsMatch(string action)
            {
                return Regex.IsMatch(action, ActionPattern);
            }
        }

        public void Add(Func<IOperationHandler, IOperationHandler> creator, string actionPattern)
        {
            Creators.Add(new HandlerInfo
            {
                ActionPattern = actionPattern,
                Creator = creator,
            });
        }

        public override IOperationHandler CreateHandlerChains(RawHandler raw)
        {
            IOperationHandler handler = raw;
            string action = OperationContext.Current.IncomingMessageHeaders.Action;
            foreach (var item in Creators)
            {
                if (item.IsMatch(action))
                {
                    handler = item.Creator(handler);
                }
            }
            return handler;
        }

    }

    通过这个简单的Factory,可以很简单的通过添加Handler,作用于那些Action符合Pattern的操作。

    为了方便演示,先定义一个简单的Handler:

        public abstract class OperationHandlerBase
            : IOperationHandler
        {

            private object m_request;
            private object m_response;
            public IOperationHandler Next { get; set; }

            public abstract void Handle();

            public object Request
            {
                get
                {
                    if (Next == null)
                        return m_request;
                    else
                        return Next.Request;
                }
                set
                {
                    if (Next == null)
                        m_request = value;
                    else
                        Next.Request = value;
                }
            }

            public object Response
            {
                get
                {
                    if (Next == null)
                        return m_response;
                    else
                        return Next.Response;
                }
                set
                {
                    if (Next == null)
                        m_response = value;
                    else
                        Next.Response = value;
                }
            }

        }

        public class LogHandler
            : OperationHandlerBase
        {

            public override void Handle()
            {
                Trace.TraceInformation("Begin action:{0}", OperationContext.Current.IncomingMessageHeaders.Action);
                // this handler can handle nothing.
                //if (false)
                //    return;
                //else
                if (Next != null)
                {
                    Next.Handle();
                }
                else
                {
                    Trace.TraceInformation("Action {0} not implemented.", OperationContext.Current.IncomingMessageHeaders.Action);
                    throw new FaultException("Not implemented.");
                }
                Trace.TraceInformation("End action:{0}", OperationContext.Current.IncomingMessageHeaders.Action);
            }

        }

    然后再把这个Handler注册到工厂中,例如:

HardCodeHandlerFactory f = new HardCodeHandlerFactory();
f.Add(handler => new LogHandler { Next = handler }, "\\S");
HandlerFactory.Current = f;

    这样,所有有Controller这个特性的WCF操作都会在开始和正常结束的时候输出2行日志(因为正则\S可以匹配和Action,如果需要选择性的注入,可以修改正则)。

    当然,因为LogHandler本身并不能处理掉任何的请求,因此,在责任链中的它必须把请求发送给下一个处理者(如果没有下一个处理者,那就是有问题的责任链,因此直接抛错)。

    但是在某些Handler中(例如负责参数检查的Handler),如果发现参数有问题,将可以直接将请求变成已经处理,从而短路后面的处理。

打造路由表——思考和下篇预告

    前面一节,已经可以简单实现HardCode的路由功能,然后可以思考一下如何通过配置的形式来更改路由表。

    思考一下路由的要素:

  • 处理器类型是如何发现的(配置文件指定等方式)
  • 处理器的创建(构造函数等约定)
  • 处理器是否需要可配置的生效(也就是HardCode中的正则的作用)
  • 环境变量(也就是前面的处理器可以修改环境值而影响后面的处理器行为)

    下一篇将解决这些问题,实现一个基于配置的WCF。

posted on 2010-11-20 17:39  Zhenway  阅读(2082)  评论(2编辑  收藏  举报