Asp.net Web Applicatoin实现自定义HttpModule拦截异常处理

        Asp.net的NamePipe机制给我们提供了很多扩展性. 使用HttpModule我们可能实现的有:

  • 强制站点范围的Cookie策略
  • 集中化监控与日志
  • 编写设置与删除HTTP头
  • 控制response输出,如删除多余空白字符
  • Session管理
  • 认证与受权

下面我们来看如何实现自定义异常处理:

   1:    public class ErrorModule:IHttpModule
   2:      {
   3:          #region IHttpModule Members
   4:   
   5:          /// <summary>
   6:          /// Disposes of the resources (other than memory) used by the module that implements <see cref="T:System.Web.IHttpModule"/>.
   7:          /// </summary>
   8:          public void Dispose()
   9:          {
  10:              //do nothing 
  11:          }
  12:   
  13:          /// <summary>
  14:          /// Initializes a module and prepares it to handle requests.
  15:          /// </summary>
  16:          /// <param name="context">An <see cref="T:System.Web.HttpApplication"/> that provides access to the methods, properties, and events common to all application objects within an ASP.NET application</param>
  17:          public void Init(HttpApplication context)
  18:          {
  19:              context.Error += new EventHandler(customcontext_Error);
  20:          }
  21:   
  22:          private void customcontext_Error(object sender, EventArgs e)
  23:          {
  24:              HttpContext ctx = HttpContext.Current;
  25:              HttpResponse response = ctx.Response;
  26:              HttpRequest request = ctx.Request;
  27:   
  28:              Exception exception = ctx.Server.GetLastError();
  29:   
  30:              var sboutput = new StringBuilder();
  31:              sboutput.Append("Querystring:<p/>");
  32:              //Get out the query string 
  33:              int count = request.QueryString.Count;
  34:              for (int i = 0; i < count; i++)
  35:              {
  36:                  sboutput.AppendFormat("<br/> {0}:-- {1} ", request.QueryString.Keys[i], request.QueryString[i]);
  37:              }
  38:              //Get out the form collection info
  39:              sboutput.Append("<p>-------------------------<p/>Form:<p/>");
  40:              count = request.Form.Count;
  41:              for (int i = 0; i < count; i++)
  42:              {
  43:                  sboutput.AppendFormat("<br/> {0}:-- {1}  -- <br/>", request.Form.Keys[i], request.Form[i]);
  44:              }
  45:              sboutput.Append("<p>-------------------------<p/>ErrorInfo:<p/>");
  46:              sboutput.AppendFormat(@"Your request could not processed. Please press the back button on your browser and try again.<br/>
  47:                                 If the problem persists, please contact technical support<p/>
  48:                                 Information below is for technical support:<p/>
  49:                                  <p/>URL:{0}<p/>Stacktrace:---<br/>{1}<p/>InnerException:<br/>{2}"
  50:           , ctx.Request.Url, exception.InnerException.StackTrace, exception.InnerException);
  51:   
  52:             response.Write(sboutput.ToString());
  53:   
  54:              // To let the page finish running we clear the error
  55:              ctx.Server.ClearError();
  56:          }
  57:   
  58:          #endregion
  59:   
  60:      }

上面的代码实现了IHttpModule接口, 实现基于HttpApplication.Error事件, 接着我们自定义输出了一些信息,包括Form,QueryString. 最后把原来的Error信息清除了,这样你看到以前那个黄页了. 这个自定义的Module可以用于调试Web应用程序使用.

Web.config中配置:

<httpModules>
      <add name="ErrorLoggingModule" type="MyWeb.ErrorModule"/>
</httpModules>

实际开发中,我们可以做的事儿很多,对这些信息记日志,发邮件. 如下, 我们演示使用Enterprise Library 做异常处理并日志记录,部分代码如下:

   1:          /// <summary>
   2:          /// Handles the Error event of the EntLibLogging control.
   3:          /// </summary>
   4:          /// <param name="sender">The source of the event.</param>
   5:          /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
   6:          /// <remarks>author Petter Liu http://wintersun.cnblogs.com</remarks>
   7:          private void EntLibLogging_Error(object sender, EventArgs e)
   8:          {
   9:              var builder = new ConfigurationSourceBuilder();
  10:   
  11:              builder.ConfigureInstrumentation()
  12:                .ForApplicationInstance("MyApp")
  13:                .EnableLogging()
  14:               .EnablePerformanceCounters();
  15:   
  16:              //a single exception handling policy named MyPolicy for exceptions of type ArgumentNullException. 
  17:              //The handler for this exception policy will log the exception to the General category (defined in the Logging Application Block configuration) 
  18:              //as a warning with event ID 9000, wrap the ArgumentNullException with an InvalidOperationException, set the new exception message to MyMessage, and then re-throw the exception.
  19:              builder.ConfigureExceptionHandling()
  20:                  .GivenPolicyWithName("MyPolicy")
  21:                  .ForExceptionType<ArgumentNullException>()
  22:                  .LogToCategory("Exception")
  23:                    .WithSeverity(System.Diagnostics.TraceEventType.Warning)
  24:                    .UsingEventId(9000)
  25:                  .WrapWith<InvalidOperationException>()
  26:                    .UsingMessage("MyMessage")
  27:                   .ThenNotifyRethrow();
  28:   
  29:              //logging application 
  30:              builder.ConfigureLogging()
  31:                 .WithOptions
  32:                 .DoNotRevertImpersonation()
  33:                 .LogToCategoryNamed("Exception")
  34:                 .SendTo.FlatFile("Exception Logging File")
  35:                  .FormatWith(new FormatterBuilder()
  36:                  .TextFormatterNamed("Text Formatter")
  37:                 . UsingTemplate("Timestamp: {timestamp}{newline}Message: {message}{newline}Category: {category}{newline}"))
  38:                  .ToFile("d:\\logs\\ExceptionsLog.log")
  39:                 .SendTo.RollingFile("Rolling Log files")
  40:                 .RollAfterSize(1024)
  41:                 .ToFile("d:\\logs\\Rollinglog.log")
  42:                 .LogToCategoryNamed("General")
  43:                 .WithOptions.SetAsDefaultCategory()
  44:                 .SendTo.SharedListenerNamed("Exception Logging File");
  45:   
  46:              var configSource = new DictionaryConfigurationSource();
  47:              builder.UpdateConfigurationWithReplace(configSource);
  48:              EnterpriseLibraryContainer.Current  = EnterpriseLibraryContainer.CreateDefaultContainer(configSource);
  49:            
  50:              var ex = HttpContext.Current.Server.GetLastError();
  51:              var em = EnterpriseLibraryContainer.Current.GetInstance<ExceptionManager>();
  52:              em.HandleException(ex.InnerException, "MyPolicy");   
  53:          }

注意上面的代码, 为了运行代码您需要引用以下程序集

Enterprise Library Share Common Library
Microsoft.Practices.ServiceLocation
Logging Application Block
Exception Handling Application Block
Exception Handling Logging Provider

这里我们使用Fluent API配置, 因此没有配置XML文件. 所以不需要Web.Config中配置任何信息. 代码中有注释. 为了测试我们使用一个PAGE故意Throw 一个ArgumentNullException. 通过Server.GetLastError()获取Exception, 这时由名为MyPolicy策略处理异常. 对于ArgumentNullException把它们记录日志到名为Exception分类中,这个日志文件位于d:\\logs\\
ExceptionLog.log.实际开发你完全按照你的需要来自定义策略.

然后这个日志文件中写出的内容是这样的:

----------------------------------------
Timestamp: 2011-11-12 5:57:08
Message: HandlingInstanceID: a99d005d-5f8d-4613-9522-2d60efb089aa
An exception of type 'System.ArgumentNullException' occurred and was caught.
----------------------------------------------------------------------------
11/12/2011 13:57:08
Type : System.ArgumentNullException, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Message : Value cannot be null.
Parameter name: Demo error
Source : MyWeb
Help link : 
ParamName : Demo error
Data : System.Collections.ListDictionaryInternal
TargetSite : Void Page_Load(System.Object, System.EventArgs)
Stack Trace :    at MyWeb.About.Page_Load(Object sender, EventArgs e) in H:\My Project\DotNet40\TDD2010\WebHost\MyWeb\About.aspx.cs:line 14
   at System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e)
   at System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e)
   at System.Web.UI.Control.OnLoad(EventArgs e)
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

Additional Info:

MachineName : USER
TimeStamp : 2011-11-12 5:57:08
FullName : Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
AppDomainName : 3e5cb21e-3-129655510216406250
ThreadIdentity : 
WindowsIdentity : USER\Petter

Category: Exception

由于我们日志模块我们配置还需要写Rollinglog.log文件,内容如下:

----------------------------------------
Exception Warning: 9000 : Timestamp: 2011-11-12 5:57:08
Message: HandlingInstanceID: a99d005d-5f8d-4613-9522-2d60efb089aa
An exception of type 'System.ArgumentNullException' occurred and was caught.
----------------------------------------------------------------------------
11/12/2011 13:57:08
Type : System.ArgumentNullException, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Message : Value cannot be null.
Parameter name: Demo error
Source : MyWeb
Help link : 
ParamName : Demo error
Data : System.Collections.ListDictionaryInternal
TargetSite : Void Page_Load(System.Object, System.EventArgs)
Stack Trace :    at MyWeb.About.Page_Load(Object sender, EventArgs e) in H:\My Project\DotNet40\TDD2010\WebHost\MyWeb\About.aspx.cs:line 14
   at System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e)
   at System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e)
   at System.Web.UI.Control.OnLoad(EventArgs e)
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

Additional Info:

MachineName : USER
TimeStamp : 2011-11-12 5:57:08
FullName : Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
AppDomainName : 3e5cb21e-3-129655510216406250
ThreadIdentity : 
WindowsIdentity : USER\Petter

Category: Exception
Priority: 0
EventId: 9000
Severity: Warning
Title:Enterprise Library Exception Handling
Machine: USER
App Domain: 3e5cb21e-3-129655510216406250
ProcessId: 2444
Process Name: C:\Program Files\Common Files\Microsoft Shared\DevServer\10.0\WebDev.WebServer40.exe
Thread Name: 
Win32 ThreadId:2748
Extended Properties: 
----------------------------------------
你可以看到上面的EventId=9000与我们之前在CODE中配置是相同的. 
在开源社区有一个组件ELMAH(Error Logging Modules and Handlers for ASP.NET),已实现Error处理发邮件,写DB,发送到SNS等功能. 我们随意来看下它的代码:
它就是基于IHttpModule的扩展,下面代码来ELMAH:
   1:      /// <summary>
   2:      /// Provides an abstract base class for <see cref="IHttpModule"/> that
   3:      /// supports discovery from within partial trust environments.
   4:      /// </summary>
   5:      public abstract class HttpModuleBase : IHttpModule
   6:      {
   7:          void IHttpModule.Init(HttpApplication context)
   8:          {
   9:              if (context == null)
  10:                  throw new ArgumentNullException("context");
  11:   
  12:              if (SupportDiscoverability)
  13:                  HttpModuleRegistry.RegisterInPartialTrust(context, this);
  14:   
  15:              OnInit(context);
  16:          }
  17:   
  18:          void IHttpModule.Dispose()
  19:          {
  20:              OnDispose();
  21:          }
  22:   
  23:          /// <summary>
  24:          /// Determines whether the module will be registered for discovery
  25:          /// in partial trust environments or not.
  26:          /// </summary>
  27:          protected virtual bool SupportDiscoverability
  28:          {
  29:              get { return false; }
  30:          }
  31:   
  32:          /// <summary>
  33:          /// Initializes the module and prepares it to handle requests.
  34:          /// </summary>
  35:          protected virtual void OnInit(HttpApplication application) {}
  36:   
  37:          /// <summary>
  38:          /// Disposes of the resources (other than memory) used by the module.
  39:          /// </summary>
  40:          protected virtual void OnDispose() {}
  41:      }
这是ErrorLogModule实现之前HttpModuleBase:
   1:  public class ErrorLogModule : HttpModuleBase, IExceptionFiltering
   2:      {
   3:          public event ExceptionFilterEventHandler Filtering;
   4:          public event ErrorLoggedEventHandler Logged;
   5:   
   6:          /// <summary>
   7:          /// Initializes the module and prepares it to handle requests.
   8:          /// </summary>
   9:          
  10:          protected override void OnInit(HttpApplication application)
  11:          {
  12:              if (application == null)
  13:                  throw new ArgumentNullException("application");
  14:              
  15:              application.Error += new EventHandler(OnError);
  16:              ErrorSignal.Get(application).Raised += new ErrorSignalEventHandler(OnErrorSignaled);
  17:          }
  18:   
  19:          /// <summary>
  20:          /// Gets the <see cref="ErrorLog"/> instance to which the module
  21:          /// will log exceptions.
  22:          /// </summary>
  23:          protected virtual ErrorLog GetErrorLog(HttpContext context)
  24:          {
  25:              return ErrorLog.GetDefault(context);
  26:          }
  27:   
  28:          /// <summary>
  29:          /// The handler called when an unhandled exception bubbles up to 
  30:          /// the module.
  31:          /// </summary>
  32:          protected virtual void OnError(object sender, EventArgs args)
  33:          {
  34:              HttpApplication application = (HttpApplication) sender;
  35:              LogException(application.Server.GetLastError(), application.Context);
  36:          }
  37:   
  38:          /// <summary>
  39:          /// The handler called when an exception is explicitly signaled.
  40:          /// </summary>
  41:          protected virtual void OnErrorSignaled(object sender, ErrorSignalEventArgs args)
  42:          {
  43:              LogException(args.Exception, args.Context);
  44:          }
  45:   
  46:          /// <summary>
  47:          /// Logs an exception and its context to the error log.
  48:          /// </summary>
  49:          protected virtual void LogException(Exception e, HttpContext context)
  50:          {
  51:              if (e == null)
  52:                  throw new ArgumentNullException("e");
  53:   
  54:              //
  55:              // Fire an event to check if listeners want to filter out
  56:              // logging of the uncaught exception.
  57:              //
  58:   
  59:              ExceptionFilterEventArgs args = new ExceptionFilterEventArgs(e, context);
  60:              OnFiltering(args);
  61:              
  62:              if (args.Dismissed)
  63:                  return;
  64:              
  65:              //
  66:              // Log away...
  67:              //
  68:   
  69:              ErrorLogEntry entry = null;
  70:   
  71:              try
  72:              {
  73:                  Error error = new Error(e, context);
  74:                  ErrorLog log = GetErrorLog(context);
  75:                  string id = log.Log(error);
  76:                  entry = new ErrorLogEntry(log, id, error);
  77:              }
  78:              catch (Exception localException)
  79:              {
  80:                  //
  81:                  // IMPORTANT! We swallow any exception raised during the 
  82:                  // logging and send them out to the trace . The idea 
  83:                  // here is that logging of exceptions by itself should not 
  84:                  // be  critical to the overall operation of the application.
  85:                  // The bad thing is that we catch ANY kind of exception, 
  86:                  // even system ones and potentially let them slip by.
  87:                  //
  88:   
  89:                  Trace.WriteLine(localException);
  90:              }
  91:   
  92:              if (entry != null)
  93:                  OnLogged(new ErrorLoggedEventArgs(entry));
  94:          }
  95:   
  96:          /// <summary>
  97:          /// Raises the <see cref="Logged"/> event.
  98:          /// </summary>
  99:          protected virtual void OnLogged(ErrorLoggedEventArgs args)
 100:          {
 101:              ErrorLoggedEventHandler handler = Logged;
 102:   
 103:              if (handler != null)
 104:                  handler(this, args);
 105:          }
 106:   
 107:          /// <summary>
 108:          /// Raises the <see cref="Filtering"/> event.
 109:          /// </summary>
 110:          protected virtual void OnFiltering(ExceptionFilterEventArgs args)
 111:          {
 112:              ExceptionFilterEventHandler handler = Filtering;
 113:              
 114:              if (handler != null)
 115:                  handler(this, args);
 116:          }
 117:   
 118:          /// <summary>
 119:          /// Determines whether the module will be registered for discovery
 120:          /// in partial trust environments or not.
 121:          /// </summary>
 122:          protected override bool SupportDiscoverability
 123:          {
 124:              get { return true; }
 125:          }
 126:      }
更多的功能等待您去挖掘,我们在实际开发中按需选择.希望这篇POST对您开发有帮助.

你可能感兴趣的文章:
在VS2010中配制Elmah邮件发送到Gmail
HttpModule应用:
使用HttpModules实现Asp.net离线应用程序
Asp.net使用HttpModule压缩并删除空白Html请求

Asp.net移除Server, X-Powered-By, 和X-AspNet-Version头
Enterprise Library:

使用Fluent配置API驱动Enterprise Library 5.0
EneterpriseLibrary5的Fluent配制API


作者:Petter Liu
出处:http://www.cnblogs.com/wintersun/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
该文章也同时发布在我的独立博客中-Petter Liu Blog

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值