现在的位置: 首页 > 综合 > 正文

小练:IHttpHandler和职责链模式

2013年01月01日 ⁄ 综合 ⁄ 共 7647字 ⁄ 字号 评论关闭

IHttpHandler不是什么新鲜的东西,大家都知道怎么用,故在本文标题是“小练”。

那么练习什么呢?

考虑:

在一个Web应用中,我们可以定义一个IHttpHandler处理程序完成一个工作,也可以定义多个IHttpHandler处理程序完成多个工作,如果这些工作互相独立,那么应该为每一个处理程序定义一个扩展名,如此大量的处理程序有可能使你感到困惑,而且系统还需要知道如何使用这些处理程序。

练习目的:

创建一个IHttpHandler处理程序来执行所有需要处理的工作,并且其本身并不知道需要处理的工作是什么。

目的说明:

我的意思是通过一个httpHandlers扩展,实现任意多个处理程序,这样就省去在配置文件里写很多system.web/httpHandlers/add

源码下载

分析:

假设成功创建了如上文所述的处理程序,则该处理程序将处理多个未知任务,对于处理程序来说,它只知道任务的参与者,而不知道参与者能完成什么任务(只有参与者自己知道能做什么)。显然,这种功能需要反射来帮忙,而且需要建一个配置文件类,把参与者一个个加进来就像appSettings中的add一样。找到参与者之后,就不难开展工作了,可以先问每个参与者都会做什么,然后就是:Do it,按照这一思路,最简单的就是再循环然中判断,为了让练习能够更有意思,我不选择这个做法,而是运用设计模式里的职责链模式来完成,即把所有参与者都“串”起来,先问第一个人能不能做,不能做再通过他询问第二个人,依次类推,直至最后一个,如果还找不到能做事的人,那只好把工作throw给“领导”了。

代码:

首先是有关配置的类

internal class HandlerSectionElement : ConfigurationElement
{
    public HandlerSectionElement()
    {
        //
        // TODO: 在此处添加构造函数逻辑
        //
    }

    [ConfigurationProperty("type", IsRequired = true)]
    [StringValidator(InvalidCharacters = "~!@#$%^&*()[]{}/;'\"|\\")]
    public string TypeString
    {
        get
        { return (string)this["type"]; }
        set
        { this["type"] = value.Trim(); }
    }

}

internal class HandlerSectionCollection : ConfigurationElementCollection
{
    public HandlerSectionCollection()
    {
        //
        // TODO: 在此处添加构造函数逻辑
        //
    }

    protected override ConfigurationElement CreateNewElement()
    {
        HandlerSectionElement element = new HandlerSectionElement();
        BaseAdd(element);
        return element;
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
        return ((HandlerSectionElement)element).TypeString;
    }

    public string[] TypeKeys
    {
        get
        {
            object[] keys = BaseGetAllKeys();

            string[] temp = new string[keys.Length];

            for (int i = 0; i < keys.Length; i++)
            {
                temp[i] = (string)keys[i];
            }

            return temp;

        }
    }

}

 

internal class HandlerSection : ConfigurationSection
    {
        public HandlerSection()
        {
            //
            // TODO: 在此处添加构造函数逻辑
            //
        }

        [ConfigurationProperty("handlers", IsDefaultCollection = false, IsRequired = true)]
        [ConfigurationCollection(typeof(HandlerSectionCollection), AddItemName = "add", ClearItemsName = "clear")]
        public HandlerSectionCollection Handlers
        {
            get
            {
                return (HandlerSectionCollection)base["handlers"];
            }

        }

    }

有了这些类就可以在Web.config中配置了

<configSections>
    <section name="chainHandlerSection" type="ChainHandlerLib.HandlerSection,ChainHandlerLib"
             allowDefinition="Everywhere" allowLocation="false"/>
</configSections>
<chainHandlerSection>
    <handlers>
        <add type="TestHandlerLibrary.TestHandler2,TestHandlerLibrary"/>
        <add type="TestHandlerLibrary.TestHandler,TestHandlerLibrary"/>
        <add type="HelloHandler"/>
    </handlers>
</chainHandlerSection>

 

接下来是获取配置数据类,通过它来读取配置节点

internal class HandlerConfig
{
    HandlerSection _sections;
    public HandlerConfig()
    {
        _sections = (HandlerSection)ConfigurationManager.GetSection("chainHandlerSection");
    }

    public HandlerSectionCollection Handlers
    {
        get
        {
            return _sections.Handlers;
        }
    }
}

 

关键步骤:定义一个抽象类ChainHandler,实现IHttpHandler,用来表示链表中的每一项,其他Handler只需实现该类即可

public abstract class ChainHandler : IHttpHandler
  {
      ChainHandler _next;

      public ChainHandler Next
      {
          get
          {
              return _next;
          }
          set
          {
              _next = value;
          }
      }

      public virtual IHttpHandler GetHttpHandler(HttpContext context, string requestType, string url, string pathTranslated)
      {
          if (Next != null)
              return Next.GetHttpHandler(context, requestType, url, pathTranslated);
          else
          {
              //已至链表尾部仍未找到适合的处理程序
              throw new NotImplementedException("未知处理程序");
          }
      }

      #region IHttpHandler 成员

      public virtual bool IsReusable { get { return false; } }

      public abstract void ProcessRequest(HttpContext context);

      #endregion
  }

GetHttpHandler默认是将请求转交到后继节点,在具体类中可以重写该方法。

下一步是编写HttpHandler的处理工厂,接收处理请求,然后交给链表

public sealed class ChainHandlerFactory : IHttpHandlerFactory
    {
        /// <summary>
        /// 链表第一个值
        /// </summary>
        ChainHandler _first;

        public ChainHandlerFactory()
        {
            _first =BuildChain();
        }

        #region IHttpHandlerFactory 成员

        public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
        {
            return _first.GetHttpHandler(context, requestType, url, pathTranslated);
        }

        public void ReleaseHandler(IHttpHandler handler)
        {
            handler = null;
        }

        #endregion

        #region 构造链表
        /// <summary>
        /// 构造链表
        /// </summary>
        /// <returns></returns>
        private ChainHandler BuildChain()
        {
            HandlerConfig config = new HandlerConfig();

            //获取类型字符串数组
            string[] typeKeys = config.Handlers.TypeKeys;

            List<ChainHandler> list = new List<ChainHandler>();

            //默认的App_Code程序集名
            string webCodeAssamblyName = "App_Code";

            //首先把assamblyName值设置为当前Web应用程序的App_Code程序集,如果
            //config里定义了assambly,将在后面设置其新值,否则使用App_Code程序集
           

//获取域中所有加载的程序集
            Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();

            //循环检查App_Code程序集
            for (int i = loadedAssemblies.Length - 1; i > 0; i--)
            {
                if (loadedAssemblies[i].FullName.StartsWith("App_Code."))
                {
                    webCodeAssamblyName = loadedAssemblies[i].FullName;
                    break;
                }
            }

            foreach (string typekey in typeKeys)
            {
                string className = string.Empty;
                //程序集名为App_Code程序集名
                string assamblyName = webCodeAssamblyName;

                string[] splitTemp = typekey.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);

                className = splitTemp[0].Trim();

                //设置新assambly值
                if (splitTemp.Length == 2)
                    assamblyName = splitTemp[1].Trim();

                //反射构造实例
                ChainHandler handler = Assembly.Load(assamblyName).CreateInstance(className) as ChainHandler;

                if (handler != null)
                    list.Add(handler);
            }

            //设置Next
            for (int i = 0; i < list.Count - 1; i++)
            {
                list[i].Next = list[i + 1];
            }

            if (list.Count < 1)
                throw new NotImplementedException("chainHandlerSection/handlers 节点未配置正确");
            return list[0];
        }
        #endregion
    }

BuildChain方法读取配置,然后通过反射构造其具体类,并添加到链表,链表的顺序和配置的顺序一致,最后通过一个循环,依次设置Next属性,将整个链表连接起来,返回链表中的第一项。GetHandler方法从第一项开始执行GetHttpHandler,即询问是否可以处理请求。

值得一提的是如何获取App_Code程序集,该程序集在发布前的网站是以App_Code.xxxx.dll形式出现的(xxxx表示随机字符),而在发布后的网站中是以App_Code.dll形式出现的。对于后一种情况比较好办,只要将程序集的名称置为“App_Code”即可,而前一种情况,似乎没有比从应用程序域加载的所有程序集中循环检查“App_Code.”来获取对应的随机程序集更好的办法。

下面看看具体的处理程序如何写

public class HelloHandler:ChainHandlerLib.ChainHandler
{
    public HelloHandler()
    {
        //
        // TODO: 在此处添加构造函数逻辑
        //
    }

    public override IHttpHandler GetHttpHandler(HttpContext context, string requestType, string url, string pathTranslated)
    {
        if (context.Request.QueryString.Count==0)
            return this;
        return base.GetHttpHandler(context, requestType, url, pathTranslated);
    }

    public override void ProcessRequest(HttpContext context)
    {
        context.Response.Write("<i><b>你好,请登录</b></i>");
    }
}

代码比较简单,重写GetHttpHandler方法:当web请求中没有QueryString字符时,返回本身,这样ChainHandlerFactory就得到了一个处理程序的实例,然后就调用HelloHandler.ProcessRequest执行请求,输出一行文字。

当然,在执行前还需要对Web.config配置

<httpHandlers>
    <add verb="*" path="*.ashx" type="ChainHandlerLib.ChainHandlerFactory,ChainHandlerLib"/>
</httpHandlers>

path可以自定义,随你喜好,此处用ashx是为了方便,不用设置IIS了。

chainhandler 

小结

上面的练习探讨了职责链模式在IHttpHandler中的应用。

职责链模式(摘录)

意图
是对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直道一个对象处理它为止。

抱歉!评论已关闭.