buguge - Keep it simple,stupid

知识就是力量,但更重要的,是运用知识的能力why buguge?

导航

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里访问接口所记录的日志:

下图是发布到iis7后访问接口所记录的日志:

 

posted on 2017-10-25 15:05  buguge  阅读(5846)  评论(0编辑  收藏  举报