隐藏

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

发布:2021/7/14 8:58:24作者:管理员 来源:本站 浏览次数:1241

     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