ASP.net global exception handling and custom error page

by Arnold Matusz 1 10 2008

This is for all those who have allready seen the yellow screen of death. If you are an ASP.net web developer this might have happened to you a few times. It is very helpful as it displays most of the information you need regarding the exception source but in a few cases this might not be enough.

For example, if an exception is thrown within an UpdatePanel the yellow screen of death won't display, instead you'll get a JavaScript alert("") from Sys.WebForms.PageRequestManagerServerErrorException which displays the Exception.Message. (In this case you don't know which object generated the exception and find yourself in a situation where you miss the yellow screen of death). Global exception handling comes in good here, it is a good place to save exception details in a database table or to show it to the developer in a nicely formatted manner.

It's not coincidence that I wrote "developer", as you might check if the site is being viewed by a developer in which case you would like to know all the details of the exception. If the request comes from a normal user you'ld need to redirect him to a friendly exception page expressing appologies for inconvenience.

How to assess if the request comes from a person who should see exception details:

  • the request comes from a specific IP address range
  • the request comes from a specific host name
  • the request comes from an authenticated user which is in a specific role (in case you use a RoleProvider).

Global exception handling can be done from the Global.asax file located in the root of your web application. Application_Error(object sender, EventArgs e) is called everytime there is a unhandled exception in the web application.
The instance of the Exception itself is Server.GetLastError().GetBaseException(). From here on you can do your work on the exception. (Ex.: save details in a database)

I have a little example here which builds up the exception text in a StringBuilder which is then displayed on the custom exception page. On the custom exception page you can decide if you display the exception text based on your criteria or you show a nice little appology message.

void Application_Error(Object sender, EventArgs e)
    {
        // Code that runs when an unhandled error occurs    
        Exception ex = Server.GetLastError().GetBaseException();
        StringBuilder sb = new StringBuilder();
        sb.AppendLine("<b>Message</b>: <b>" + ex.Message.ToString() + "</b><br />");
        sb.AppendLine("<b>Exception</b>: " + ex.GetType().ToString() + "<br />");
        if (ex.TargetSite != null)
            sb.AppendLine("<b>Targetsite</b>: " + ex.TargetSite.ToString() + "<br />");
        sb.AppendLine("<b>Source</b>: " + ex.Source + "<br />");
        sb.AppendLine("<b>StackTrace</b>: " + ex.StackTrace.ToString().
                                      Replace(Environment.NewLine, "<br />") + "<br />");
        sb.AppendLine("<b>Data count</b>: " + ex.Data.Count.ToString() + "<br />");
        if (ex.Data.Count > 0)
        {
            HtmlTable tbl = new HtmlTable();
            tbl.Border = 1;
            HtmlTableRow htr = new HtmlTableRow();            
            HtmlTableCell htc1 = new HtmlTableCell();
            HtmlTableCell htc2 = new HtmlTableCell();
            HtmlTableCell htc3 = new HtmlTableCell();
            HtmlTableCell htc4 = new HtmlTableCell();
            htc1.InnerHtml = "<b>Key</b>";
            htc2.InnerHtml = "<b>Value</b>";
            htc3.InnerHtml = "Key Type";
            htc4.InnerHtml = "Value Type";
                        
            htr.Cells.Add(htc1);
            htr.Cells.Add(htc2);
            htr.Cells.Add(htc3);
            htr.Cells.Add(htc4);
            tbl.Rows.Add(htr);
            
            foreach (DictionaryEntry de in ex.Data)
            {
                HtmlTableRow tr = new HtmlTableRow();
                HtmlTableCell tc1 = new HtmlTableCell();
                HtmlTableCell tc2 = new HtmlTableCell();
                HtmlTableCell tc3 = new HtmlTableCell();
                HtmlTableCell tc4 = new HtmlTableCell();
                tc1.InnerHtml = "<b>" + de.Key + "</b>";
                tc2.InnerHtml = "<b>" + de.Value + "</b>";
                tc3.InnerHtml = de.Key.GetType().Name;
                tc4.InnerHtml = de.Value.GetType().Name;
                tc3.Align = "center";
                tc4.Align = "center";
                
                tr.Cells.Add(tc1);
                tr.Cells.Add(tc2);
                tr.Cells.Add(tc3);
                tr.Cells.Add(tc4);
                tbl.Rows.Add(tr);
            }
            StringBuilder tblSb = new StringBuilder();
            System.IO.StringWriter sw = new System.IO.StringWriter(tblSb);
            HtmlTextWriter htw = new HtmlTextWriter(sw);
            tbl.RenderControl(htw);
            sb.AppendLine(tblSb.ToString());
        }
        sb.AppendLine("<br />");
        sb.AppendLine("<b>Exception</b>: " + ex.ToString().
                                      Replace(Environment.NewLine, "<br />") + "<br />");
        Session["Exception"] = sb.ToString();
        Server.ClearError();
        Response.Redirect("~/exception.aspx");
    } 

Some of you may have noticed that I'm using a Response.Redirect() instead of a Server.Transfer(), and this of course happened intentionally.  Features of Redirect() and Transfer() drive the decision. Redirect creates a new Context, Transfer does not. Redirect requires a round-trip to the browser, Transfer does not. As a result of this round-trip, Redirect rewrites the URL to reflect the location of the error page, Transfer does not.
As you may work on an ASP.NET AJAX enabled website, if the exception is generated within an UpdatePanel Server.Transfer() will render the content of a complete page whereas the UpdatePanel will expect only a chunck of HTML. Using Server.Transfer() in an UpdatePanel will result in a JavaScript alert() message by Sys.WebForms.PageRequestManagerServerErrorException.

I've attached an archive containing a sample global.asax file and a sample custom error page: ASP.net global exception handling and custom error page.zip.

I'm looking forward to seeing other solutions for global exception handling or custom error pages from anybody interested in sharing.

DZone it Digg it Submit to StumbleUpon Submit to Technorati Submit to reddit.com Submit to del.icio.us Submit to NewsVine Submit to Furl Submit to BlinkList

Tags: , ,

ASP.NET | C# | Programming

Comments

10/15/2008 2:45:16 AM #

trackback

Trackback from DotNetKicks.com

ASP.net global exception handling and custom error page

DotNetKicks.com |

11/17/2009 10:47:23 PM #

Aram

Gooooooooood !!!

Aram Russia |