|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionThis article is a follow-up to my previous CodeProject article, User Friendly Exception Handling, which covered global exception handling for console and WinForms apps. As I mentioned in that article:
This article covers a similar global exception handling technique for ASP.NET applications and Web Services. I won't go over any of the background on exceptions, as that was covered in the introduction to the previous article. Let's jump right into the implementation. Unhandled Exceptions in ASP.NET WebsitesThere are two approaches you can use when you set out to design a global exception handler in ASP.NET. The first method is the most straightforward: using the Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
Dim ueh As New ASPUnhandledException.Handler
ueh.HandleException(Server.GetLastError.GetBaseException())
End Sub
Most ASP.NET developers are familiar with global.asax, so this method is easy to explain. It's also the method I used in the previous version of this article. While you can still do this, there is a better way. And that second, better way is to hook into the ASP.NET HTTP pipeline by implementing Public Class UehHttpModule
Implements IHttpModule
Public Sub Init(ByVal Application As System.Web.HttpApplication)
Implements System.Web.IHttpModule.Init
AddHandler Application.Error, AddressOf OnError
End Sub
Public Sub Dispose() Implements System.Web.IHttpModule.Dispose
End Sub
Protected Overridable Sub OnError(ByVal sender As Object,
ByVal args As EventArgs)
Dim app As HttpApplication = CType(sender, HttpApplication)
Dim ueh As New ASPUnhandledException.Handler
ueh.Handle(app.Server.GetLastError)
End Sub
End Class
This is functionally identical to the first method, with one key difference: you can now implement global exception handling without recompiling the ASP.NET application! This means you can retrofit unhandled exception handling to any ASP.NET website with ease -- just copy the ASPUnhandledException.dll file to the \bin folder, then make a minor edit to the Web.config: <system.web>
<httpModules>
<add name="UehHttpModule"
type="ASPUnhandledException.UehHttpModule, ASPUnhandledException" />
</httpModules>
</system.web>
And off you go. Isn't that cool? Unhandled Exceptions in ASP.NET Web ServicesUnfortunately, neither of these methods will work for a .NET Web Service. Public Class UehSoapExtension
Inherits SoapExtension
Private _OldStream As Stream
Private _NewStream As Stream
Public Overloads Overrides Function GetInitializer( _
ByVal serviceType As System.Type) As Object
Return Nothing
End Function
Public Overloads Overrides Function GetInitializer( _
ByVal methodInfo As System.Web.Services.Protocols.LogicalMethodInfo, _
ByVal attribute As System.Web.Services.Protocols.SoapExtensionAttribute)
As Object
Return Nothing
End Function
Public Overrides Sub Initialize(ByVal initializer As Object)
End Sub
Public Overrides Function ChainStream(ByVal stream As Stream) As Stream
_OldStream = stream
_NewStream = New MemoryStream
Return _NewStream
End Function
Private Sub Copy(ByVal fromStream As Stream, ByVal toStream As Stream)
Dim sr As New StreamReader(fromStream)
Dim sw As New StreamWriter(toStream)
sw.Write(sr.ReadToEnd())
sw.Flush()
End Sub
Public Overrides Sub ProcessMessage(ByVal message _
As System.Web.Services.Protocols.SoapMessage)
Select Case message.Stage
Case SoapMessageStage.BeforeDeserialize
Copy(_OldStream, _NewStream)
_NewStream.Position = 0
Case SoapMessageStage.AfterSerialize
If Not message.Exception Is Nothing Then
Dim ueh As New Handler
Dim strDetailNode As String
'-- handle our exception, and get the SOAP <detail> string
strDetailNode = ueh.HandleWebServiceException(message)
'-- read the entire SOAP message stream into a string
_NewStream.Position = 0
Dim tr As TextReader = New StreamReader(_NewStream)
'-- insert our exception details into the string
Dim s As String = tr.ReadToEnd
s = s.Replace("<detail />", strDetailNode)
'-- overwrite the stream with our modified string
_NewStream = New MemoryStream
Dim tw As TextWriter = New StreamWriter(_NewStream)
tw.Write(s)
tw.Flush()
End If
_NewStream.Position = 0
Copy(_NewStream, _OldStream)
End Select
End Sub
End Class
The <webServices>
<soapExtensionTypes>
<add type="ASPUnhandledException.UehSoapExtension, ASPUnhandledException"
priority="1" group="0" />
</soapExtensionTypes>
</webServices>
Configuring AspUnhandledExceptionThe <configSections>
<section name="UnhandledException"
type="System.Configuration.NameValueSectionHandler, System,
Version=1.0.5000.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" />
</configSections>
<UnhandledException>
<add key="ContactInfo" value="Ima Testguy at 123-555-1212" />
<add key="EmailTo" value="me@mydomain.com" />
<add key="SmtpDefaultDomain" value="mydomain.com" />
<add key="SmtpServer" value="mail.mydomain.com" />
</UnhandledException>
This is the complete list of configuration settings:
Once you've copied the .dll file to the /bin folder, and checked your Web.config settings, everything else is automatic from this point on. ASPUnhandledException with ASP.NET websitesOkay, so now that you've hooked this up, what do you get for your troubles? Well, whenever an unhandled exception occurs in your website or web service, one or more of the following will automatically happen:
All of these options will contain the same detailed diagnostic information about the exception, which is generated by If
If
The exception email contains:
If If ASPUnhandledException with ASP.NET Web ServicesIn my previous article, I bemoaned the lack of good global error handling provisions for ASP.NET Web Services. Well, not any more. Using a custom
The only tricky part left is inserting the rich exception information we've come to expect from our ASP.NET exceptions into the SOAP message. By default, the <soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>SoapException</faultstring>
<detail/>
</soap:Fault>
Hard to diagnose this exception with so little information. That's where the Public Function HandleWebServiceException(ByVal sm As _
System.Web.Services.Protocols.SoapMessage) As String
_blnLogToUI = False
HandleException(sm.Exception)
Dim doc As New Xml.XmlDocument
Dim DetailNode As Xml.XmlNode = doc.CreateNode(XmlNodeType.Element, _
SoapException.DetailElementName.Name, _
SoapException.DetailElementName.Namespace)
Dim TypeNode As Xml.XmlNode = doc.CreateNode(XmlNodeType.Element, _
"ExceptionType", _
SoapException.DetailElementName.Namespace)
TypeNode.InnerText = _strExceptionType
DetailNode.AppendChild(TypeNode)
Dim MessageNode As Xml.XmlNode = doc.CreateNode(XmlNodeType.Element, _
"ExceptionMessage", _
SoapException.DetailElementName.Namespace)
MessageNode.InnerText = sm.Exception.Message
DetailNode.AppendChild(MessageNode)
Dim InfoNode As Xml.XmlNode = doc.CreateNode(XmlNodeType.Element, _
"ExceptionInfo", _
SoapException.DetailElementName.Namespace)
InfoNode.InnerText = _strException
DetailNode.AppendChild(InfoNode)
Return DetailNode.OuterXml.ToString()
End Function
The string returned from this method is used to modify the SOAP message "in flight", and we get a much better <soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>Server was unable to process request. -->
I trimmed many of the ConclusionOne final observation on Unhandled Exceptions, before we close our discussion. You must avoid exceptions in the Unhandled Exception Handler. This is a special case handler, the "handler of last resort", and exceptions in this code are extremely bad form. They can cause the code to terminate with no warning whatsoever, as if you put in an arbitrary I've used this class on about a dozen different websites and web services so far, with excellent results. There are many more details and comments in the source code provided at the top of the article, so check it out. Please don't hesitate to provide feedback, good or bad! I hope you enjoyed this article. If you did, you may also like my other articles as well. History
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||