一
攔截器又稱過濾器。
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裡通路接口所記錄的日志: