使用 Web.config 配置异常






3.25/5 (4投票s)
2004年4月21日
3分钟阅读

49857

395
一篇关于使用自定义处理程序、自定义配置节处理异常的文章,使其独立于使用它的应用程序。
引言
在大型企业环境中处理异常一直是个难题,您希望以不同的方式处理不同的异常。虽然可以在 web.config 中配置错误页面,但这仅限于 HTTP 特定的错误。您无法定义具有相应处理程序的特定异常类型。我在网上找到的一个解决方案是 异常管理块。但对我来说,这毕竟是一个复杂的解决方案。于是我设计了自己的机制,在 web.config 中配置异常类型及其相应的处理程序。
问题定义
通常,在简单的应用程序中,我们在 Try
- Catch
块中处理异常,如下所示:
Try
// Do some code here
Catch sqle as SQLExceptions
// Handle SQLException differently
Catch e as Exceptions
// Handle Generic Exception differently
End Try
这对于简单的应用程序来说是可以的,但对于企业应用程序来说,其中有许多页面可能会引发异常,单独配置每个块可能会让人头疼。此外,如果您派生了自己的应用程序特定异常,则必须单独处理它们,并且如果您想更改异常处理方式,则必须深入到您的 aspx 页面中。
建议解决方案
为了克服这个问题,我定义了一个自定义配置节,该配置节定义了异常类型、其错误处理程序(程序集和类)以及重定向 URL。通过这种方式,您可以在不干预之前编写的代码的情况下,以独立的方式配置异常、其处理程序(程序集和类)以及重定向 URL。
配置和处理异常
首先,我们必须在 web.config 中 <configuration>
标记的正下方定义一个自定义配置节。
<configSections >
<section name="exceptionManagers"
type="ExceptionManagers.ExceptionManagersHandler,ExceptionManagers" />
</configSections>
在 <configuration>
标记结束之前,写入此内容:
<exceptionManagers>
<exceptionManager application="MyApplication">
<exceptions>
<exception name="System.SQLException" url="ErrorPage.aspx">
<exceptionHandler assembly="ExceptionManagers"
name="SQLExceptionHandler" />
</exception>
</exceptions>
</exceptionManager>
<exceptionManager application="MyApplication">
<exceptions>
<exception name="System.InvalidOperationException"
url="ErrorPage.aspx">
<exceptionHandler assembly="ExceptionManagers"
name="InvalidOperationExceptionHandler" />
</exception>
</exceptions>
</exceptionManager>
</exceptionManagers>
上面的节定义了一个类型为 System.SQLException
的异常,如果该异常在应用程序 "MyApplication" 中引发,则将由 ExceptionManagers
程序集中的 SQLExceptionHandler
类处理,并在处理后重定向到 ErrorPage.aspx。您可以通过复制 <ExceptionManager>
标记并设置其属性来定义所有其他要处理的异常。通过以这种方式定义处理程序,我们可以将异常处理逻辑与编写在代码(Try
- Catch
块)中区分开来。我们所要做的就是在 Try
- Catch
块中添加一行代码,如下所示:
Try
//Do Some Code here , that raises Exception
Catch sqle as SQLException
Response.Redirect( ExceptionManagersHandler.PublishException("MyApplication",_
sqle) )
Catch exc as Exception
Response.Redirect(ExceptionManagersHandler.PublishException("MyApplication",_
exc) )
End Try
这行代码简单地调用 ExceptionManagersHandler
类中的共享方法 PublicshException
,并将引发异常的应用程序名称和异常对象本身传递给它。
使用代码
要定义异常处理程序,请创建一个类库项目并将其命名为 ExceptionManagers
(即 ExceptionHandler
标记中的程序集名称)。现在,添加一个名为 SQLExceptionHandler
的类来处理 SQLException
。此类实现了 IExceptionHandler
接口,该接口定义了一个名为 executeHandler
的方法。当引发 SQLException
类型的异常并且调用 ExceptionManager.PublishException
时,它会从 web.config 中检索配置设置,并搜索适当的异常处理程序。然后,该方法使用 Reflection
命名空间来实例化一个处理程序并将其调用委托给它。上述类的代码如下:
' This Class implements the IConfigurationSectionHandler interface and
' reads the configuration settings and returns a hashtable based on the
' current settings in web.config
Option Strict On
Imports System
Imports System.Collections
Imports System.Xml
Imports System.Configuration
Imports System.Reflection
Public Class ExceptionManagersHandler
Implements IConfigurationSectionHandler
'This method reads the configuration section of
'the the web.config and returns a hashtable
'populated with all the information od exceptions and their handlers
' Hashtable contains ExceptionInfo class's objects
Public Function Create(ByVal parent As Object, _
ByVal configContext As Object, ByVal section As XmlNode) _
As Object Implements IConfigurationSectionHandler.Create
Dim _HandlerBucket As New Hashtable()
Dim appName, exceptionName, redirectUrl, _
errorHandler, assemblyName As String
Dim managerNode, exceptionNode, handlerNode As XmlNode
Dim managersNL, exceptionsNL, handlersNL As XmlNodeList
Dim exceptionsList As Hashtable
Dim excepInfo As ExceptionInfo
managersNL = section.SelectNodes("//exceptionManager")
For Each managerNode In managersNL
appName = managerNode.Attributes.GetNamedItem("application").Value
exceptionsNL = managerNode.SelectNodes("exceptions/exception")
For Each exceptionNode In exceptionsNL
exceptionsList = New Hashtable()
exceptionName = exceptionNode.Attributes.GetNamedItem("name").Value
redirectUrl = exceptionNode.Attributes.GetNamedItem("url").Value
excepInfo = New ExceptionInfo(appName, exceptionName, redirectUrl)
handlersNL = exceptionNode.SelectNodes("exceptionHandler")
For Each handlerNode In handlersNL
assemblyName = handlerNode.Attributes.GetNamedItem("assembly").Value
errorHandler = handlerNode.Attributes.GetNamedItem("name").Value
Next
excepInfo.HandlerAssembly = assemblyName
excepInfo.HandlerName = errorHandler
exceptionsList.Add(exceptionName, excepInfo)
Next
_HandlerBucket.Add(appName, exceptionsList)
Next
Return _HandlerBucket
End Function
' This method is called in all the catch blocks of your application
' It returns a URL back to the calling application
Public Shared Function PublishException(ByVal appName _
As String, ByVal e As Exception) As String
If (appName Is Nothing) Then
appName = "GenericAppication"
End If
Dim expName As String = e.GetType.ToString
' Read the congiuration hashtable
Dim handlerBucket As Hashtable = _
CType(System.Configuration.ConfigurationSettings.GetConfig("exceptionManagers"), _
Hashtable)
'Find , is an appropriate handler is available for the
'application that raised exception
Dim exceptionHT As Hashtable = _
CType(handlerBucket.Item(appName), Hashtable)
Dim excepInfo As ExceptionInfo
If Not (exceptionHT Is Nothing) Then
' If application hashtable is not empty,
' then find the raised exception by name
excepInfo = CType(exceptionHT.Item(expName), ExceptionInfo)
Dim assemName, className As String
Dim classType As Type
Try
' Load the Assembly
assemName = excepInfo.HandlerAssembly
className = assemName & "." & excepInfo.HandlerName
Dim assem As [Assembly] = [Assembly].Load(assemName)
' Load the handler class
classType = assem.GetType(className)
Dim objref As Object = Activator.CreateInstance(classType)
' Cast the object to its interface, so that all exceptions
' can be published by a single call
' i have used command pattern here
Dim handler As IExceptionHandler = _
CType(objref, IExceptionHandler)
handler.executeHandler(e)
Catch exp As Exception
throw new Exception ("Unknown Exception" & _
" Occured in loading or instantiating handler" & exp.Message)
End Try
End If
Return excepInfo.RedirectUrl
End Function
End Class
IExcceptionHandler
和 ExceptionInfo
类的代码
' IExceptionHandler Interface
Public Interface IExceptionHandler
Sub executeHandler(ByVal except As Exception)
End Interface
'ExceptionInfo class , that is populated with the information in the web.config
Public Class ExceptionInfo
'Mandatory Fields
Private _ExceptionName As String
Private _RedirectUrl As String
Private _ApplicationName As String
'Optional Fields
Private _HandlerName As String
Private _HandlerAssembly As String
Public Sub New(ByVal applicationName As String, _
ByVal exceptionName As String, ByVal errorUrl As String)
_ApplicationName = applicationName
_ExceptionName = exceptionName
_RedirectUrl = errorUrl
End Sub
Public ReadOnly Property ExceptionName() As String
Get
Return _ExceptionName
End Get
End Property
Public ReadOnly Property RedirectUrl() As String
Get
Return _RedirectUrl
End Get
End Property
Public ReadOnly Property ApplicationName() As String
Get
Return _ApplicationName
End Get
End Property
Public Property HandlerName() As String
Get
Return _HandlerName
End Get
Set(ByVal Value As String)
_HandlerName = Value
End Set
End Property
Public Property HandlerAssembly() As String
Get
Return _HandlerAssembly
End Get
Set(ByVal Value As String)
_HandlerAssembly = Value
End Set
End Property
End Class
结论
通过这种方式处理异常,用户可以将异常处理代码写在 catch
块之外,这样,如果您想改变异常的处理方式,就不必深入到应用程序的代码中,只需编写一个新的处理程序或修改之前的处理程序,它就会被您的应用程序使用(前提是相关的属性已相应更新)。此外,它使您能够以独立的方式定义和引发自己的异常并编写其处理程序。您还可以配置应用程序特定的处理程序,或者简单地编写一个通用处理程序来以类似的方式处理多个异常。
关注点
我使用了 命令模式
结合反射来实现一个独立于使用应用程序的异常处理机制。虽然我对模式了解不多 :), 但我确实喜欢玩转它们。