小練:IHttpHandler和職責鏈模式

來源:互聯網
上載者:User

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了。

 

小結

上面的練習探討了職責鏈模式在IHttpHandler中的應用。

職責鏈模式(摘錄)

意圖
是對象都有機會處理請求,從而避免請求的寄件者和接受者之間的耦合關係。將這些對象連成一條鏈,並沿著這條鏈傳遞請求,直道一個對象處理它為止。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.