一
拦截器又称过滤器。
asp.net mvc本身是自带3种拦截器:Action拦截器、Result拦截器、Exception拦截器。 应用中常见的拦截器有日志拦截器(Action拦截器)和异常处理拦截器(Exception拦截器)。
java里spring mvc也常用拦截器来做些非干预业务逻辑的事,诸如实现HandlerInterceptor接口。
拦截器是AOP(面向切面编程)的一种应用。
拦截器要解决的问题:
二
/// <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里访问接口所记录的日志:
下图是发布到iis7后访问接口所记录的日志: