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。