天天看點

asp.net攔截器

攔截器又稱過濾器。

asp.net mvc本身是自帶3種攔截器:Action攔截器、Result攔截器、Exception攔截器。 應用中常見的攔截器有日志攔截器(Action攔截器)和異常處理攔截器(Exception攔截器)。

java裡spring mvc也常用攔截器來做些非幹預業務邏輯的事,諸如實作HandlerInterceptor接口。

攔截器是AOP(面向切面程式設計)的一種應用。

攔截器要解決的問題:

1.代碼複用。攔截器可被複用

2.職責單一。比如廚師隻負責炒菜,不管前期的洗菜、後續的送菜工作。菜變質了也是直接喊一聲就有人來處理。

asp.net的攔截器怎麼實作呢?

舊瓶裝新酒,asp.net的攔截器需要通過IHttpModule接口來實作。

這兩天重構支付中心代碼,将設定線程名和IP白名單這2個功能做成攔截器。

如下是線程名Filter的代碼:

/// <summary>
    /// 設定目前工作線程的名稱。供用來統一辨別記錄的日志
    /// </summary>
    public class ThreadNameFilter : IHttpModule
    {
        LogHelperUtil logHelper = new LogHelperUtil(typeof(ThreadNameFilter).Name);

        public void Dispose()
        {
            //throw new NotImplementedException();
        }

        public void Init(HttpApplication context)
        {
            //NewMethod(context);請求在此上下文中不可用

            context.BeginRequest += context_BeginRequest;
        }

        /// <summary>
        /// 設定目前工作線程的name
        /// </summary>
        /// <param name="sender"></param>
        private void SetThreadName(object sender)
        {

            if (null != Thread.CurrentThread.Name)
            {
                return;
            }

            HttpApplication application = (HttpApplication)sender;
            HttpRequest request2 = application.Context.Request;
            HttpResponse response = application.Context.Response;
            string url = request2.Url.LocalPath;
            url = url.Trim('/');

            // * 根據請求url得到一個nameFlag
            string nameFlag;
            if (url.IndexOf(".ashx", StringComparison.OrdinalIgnoreCase) > 0)
            {
                var arr = url.Split('/');
                string ashxName = arr.FirstOrDefault(str => str.IndexOf(".ashx", StringComparison.OrdinalIgnoreCase) > 0);
                nameFlag = ashxName.Substring(0, ashxName.IndexOf('.'));
                if (nameFlag == "AgentPayQuery")
                {
                    nameFlag = "QueryAgentPay";
                }
            }
            else
            {
                nameFlag = url.Replace('/', '_').Replace('.', '_');
            }

            // * 設定目前工作線程的name
            Thread.CurrentThread.Name = string.Format("[{0}_T{1:HHmmssfff}_{2}]", nameFlag, DateTime.Now, Guid.NewGuid().ToString().Replace("-", "").Substring(0, 5).ToUpper());
            logHelper.Write("線程名已設定為:{0} url:{1}", Thread.CurrentThread.Name, url);
        }

        void context_BeginRequest(object sender, EventArgs e)
        {
            SetThreadName(sender);
        }

    }      

接下來,web.config配置此module:

可在<system.web>節點下的<httpModules>裡配置,也可在<system.webServer>節點下的<modules>裡配置。  這取決于應用程式池的托管管道模式。經典模式用前者,內建模式用後者。

本地vs2013裡的iisexpress預設是內建模式。是以,本地vs2013調試程式要在<system.webServer>裡配置module。

<system.webServer>
    <modules> <!--runAllManagedModulesForAllRequests="true"-->
      <add name="threadNameFilter" type="PaymentPlatform.Filters.ThreadNameFilter" preCondition="managedHandler" />
      <add name="ipValidationInterceptor" type="PaymentPlatform.Filters.IPValidationInterceptor" preCondition="managedHandler"/> <!--隻對托管資源起作用-->
    </modules> 
    <handlers>
      。。。。。。
    </handlers>
  </system.webServer>      

這樣,一個攔截器的開發就完成了。

在後續的測試時,出現了一些波折。

本地在啟動vs2013執行iisexpress站點應用程式時,發現明明在ThreadNameFilter 裡設定了線程名,但觀察在後續ashx裡記錄的日志裡,并沒有擷取到那個線程名,whatever in Debug or in Release。這讓我想到之前寫的一篇部落格《​​巧用CurrentThread.Name來統一辨別日志記錄(續)​​》,在ashx檔案的預設構造器裡設定的線程名,在其ProcessRequest方法的處理邏輯裡也是擷取不到的。

經多次鼓搗,才發現,把站點程式釋出到IIS7上之後,無論apppool的托管模式是內建(目前,內建模式是主流)還是經典,在ThreadNameFilter 裡設定的線程名可以被後續ashx裡擷取到!

同樣,細心的觀察了一下,《​​巧用CurrentThread.Name來統一辨別日志記錄(續)​​》裡提到的問題,釋出到IIS7後也不存在,即在ashx檔案的預設構造器裡設定的線程名,在其ProcessRequest方法的處理邏輯裡可以擷取到!

下圖是本地vs2013裡通路接口所記錄的日志:

asp.net攔截器

繼續閱讀