一
攔截器又稱(chēng)過(guò)濾器。
asp.net mvc本身是自帶3種攔截器:Action攔截器、Result攔截器、Exception攔截器。 應(yīng)用中常見(jiàn)的攔截器有日志攔截器(Action攔截器)和異常處理攔截器(Exception攔截器)。
java里spring mvc也常用攔截器來(lái)做些非干預(yù)業(yè)務(wù)邏輯的事,諸如實(shí)現(xiàn)HandlerInterceptor接口。
攔截器要解決的問(wèn)題:
1.代碼復(fù)用。攔截器可被復(fù)用
2.職責(zé)單一。比如廚師只負(fù)責(zé)炒菜,不管前期的洗菜、后續(xù)的送菜工作。菜變質(zhì)了也是直接喊一聲就有人來(lái)處理。
asp.net的攔截器怎么實(shí)現(xiàn)呢?
舊瓶裝新酒,asp.net的攔截器需要通過(guò)IHttpModule接口來(lái)實(shí)現(xiàn)。
二
這兩天重構(gòu)支付中心代碼,將設(shè)置線(xiàn)程名和IP白名單這2個(gè)功能做成攔截器。
如下是線(xiàn)程名Filter的代碼:
/// <summary>
/// 設(shè)置當(dāng)前工作線(xiàn)程的名稱(chēng)。供用來(lái)統(tǒng)一標(biāo)識(shí)記錄的日志
/// </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);請(qǐng)求在此上下文中不可用
context.BeginRequest += context_BeginRequest;
}
/// <summary>
/// 設(shè)置當(dāng)前工作線(xiàn)程的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('/');
// * 根據(jù)請(qǐng)求url得到一個(gè)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('.', '_');
}
// * 設(shè)置當(dāng)前工作線(xiàn)程的name
Thread.CurrentThread.Name = string.Format("[{0}_T{1:HHmmssfff}_{2}]", nameFlag, DateTime.Now, Guid.NewGuid().ToString().Replace("-", "").Substring(0, 5).ToUpper());
logHelper.Write("線(xiàn)程名已設(shè)置為:{0} url:{1}", Thread.CurrentThread.Name, url);
}
void context_BeginRequest(object sender, EventArgs e)
{
SetThreadName(sender);
}
}
接下來(lái),web.config配置此module:
可在<system.web>節(jié)點(diǎn)下的<httpModules>里配置,也可在<system.webServer>節(jié)點(diǎn)下的<modules>里配置。 這取決于應(yīng)用程序池的托管管道模式。經(jīng)典模式用前者,集成模式用后者。
本地vs2013里的iisexpress默認(rèn)是集成模式。所以,本地vs2013調(diào)試程序要在<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"/> <!--只對(duì)托管資源起作用-->
</modules>
<handlers>
。。。。。。
</handlers>
</system.webServer>
這樣,一個(gè)攔截器的開(kāi)發(fā)就完成了。
在后續(xù)的測(cè)試時(shí),出現(xiàn)了一些波折。
本地在啟動(dòng)vs2013執(zhí)行iisexpress站點(diǎn)應(yīng)用程序時(shí),發(fā)現(xiàn)明明在ThreadNameFilter 里設(shè)置了線(xiàn)程名,但觀察在后續(xù)ashx里記錄的日志里,并沒(méi)有獲取到那個(gè)線(xiàn)程名,whatever in Debug or in Release。這讓我想到之前寫(xiě)的一篇博客《巧用CurrentThread.Name來(lái)統(tǒng)一標(biāo)識(shí)日志記錄(續(xù))》,在ashx文件的默認(rèn)構(gòu)造器里設(shè)置的線(xiàn)程名,在其ProcessRequest方法的處理邏輯里也是獲取不到的。
經(jīng)多次鼓搗,才發(fā)現(xiàn),把站點(diǎn)程序發(fā)布到IIS7上之后,無(wú)論apppool的托管模式是集成(目前,集成模式是主流)還是經(jīng)典,在ThreadNameFilter 里設(shè)置的線(xiàn)程名可以被后續(xù)ashx里獲取到!
同樣,細(xì)心的觀察了一下,《巧用CurrentThread.Name來(lái)統(tǒng)一標(biāo)識(shí)日志記錄(續(xù))》里提到的問(wèn)題,發(fā)布到IIS7后也不存在,即在ashx文件的默認(rèn)構(gòu)造器里設(shè)置的線(xiàn)程名,在其ProcessRequest方法的處理邏輯里可以獲取到!
下圖是本地vs2013里訪(fǎng)問(wèn)接口所記錄的日志:
下圖是發(fā)布到iis7后訪(fǎng)問(wèn)接口所記錄的日志: