Tip/Trick: Show Detailed ASP.NET Error Messages to Developers (and only to Developers)

Problem: You are developing/maintaining an ASP.NET web-site, and would like the ability to conditionally show/hide runtime error messages depending on who the user visiting the site is.

For a normal user visiting the site you want to be able to display a friendly error message like this when a runtime error occurs:

But when someone within the “developers” security role of your application remotely accesses the site you want to instead show a more detailed exception stack trace error message about the problem without having to change any configuration data:

The below post describes how to use ASP.NET’s role-based security architecture in conjunction with the Global.asax Application_Error event handler to enable this.  You can also download a sample I’ve built that shows how to implement this here.

Some Background Discussion on Error Handling and ASP.NET Custom Error Pages:

ASP.NET and .NET support a rich error-handling architecture that provides a flexible way to catch/handle errors at multiple levels within an application.  Specifically, you can catch and handle a runtime exception with a class, within a page, or on the global application level using the Application_Error event handler within the Global.asax class.  If a runtime exception isn’t handled/cancelled by one of these mechanisms, then ASP.NET’s Custom Error Page feature will kick-in, and an error page will be sent back to the browser accessing the application.

ASP.NET’s Custom Error Page feature can be used to configure a “friendly error page” to be displayed to end-users in place of the standard “server error occurred” message sent back by ASP.NET.  For example, the below web.config file section will cause remote users visiting the site to be redirected to a “friendlyErrorPage.htm” file anytime a runtime error occurs (note: HTTP 500 status code responses indicate runtime errors on the server):

      <customErrors mode="RemoteOnly">

        <error statusCode="500" redirect="friendlyErrorPage.htm"/>

      </customErrors>

To learn more about how the ASP.NET Custom Errors feature works, and how to configure it, please review this article.

Important: I would recommend never setting the <customErrors> mode attribute to “Off”.  Doing this will cause detailed error messages to be sent back to all normal users visiting your site.  This can lead to information disclosure issues that can compromise the security of your site.  Instead, only change this setting to “On” or “RemoteOnly” (the default) and use the technique below for cases where you want to display detailed error messages to only some users.

Solution:

The above <customErrors> configuration section will cause a friendly error message to be sent to the browser anytime a runtime error message occurs.  This is what we want to happen anytime a normal user access the site, and will allow us to display error pages like this:

To enable developers to instead see detailed error messages when they access the site, I can then add this code to the Application_Error event handler within the Global.asax class:

    Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)

               

        If (Context IsNot Nothing) And (Context.User.IsInRole("Developer")) Then

           

            Dim err As Exception = Server.GetLastError()

           

            Response.Clear()

           

            Response.Write("<h1>" & err.InnerException.Message & "</h1>")

            Response.Write("<pre>" & err.ToString & "</pre>")

           

            Server.ClearError()

           

        End If

       

    End Sub

The above code checks to see if the current user visiting the site is within a specific security role.  If so, then it retrieves the exception raised during the current request via the Server.GetLastError() method.  It then clears out any content already generated during the current request, and instead outputs the Exception details to the response.  Lastly, it clears out the Exception from the request – which will prevent ASP.NET’s custom error architecture from kicking in.  The result instead is a page that details the error like so:

To learn more about how you can easily create and manage a “Developer” role like the one I’m using above, please review my previous post: Implementing Role Based Security with ASP.NET using Windows Authentication and SQL Server.  If you don’t want to store your role-mappings within a database, you can also store them within Active Directory or your own custom role-provider. 

This sample demonstrates using a Windows User’s membership within the local “Administrators” group on a machine to control whether or not the detailed error message is displayed.  Note: you can perform a role-check on a local Windows group by writing: User.IsInRole(“BUILTIN\Administrators”) – where the “BUILTIN” prefix indicates that it is a local group as opposed to a domain level one). 

Hope this helps,

Scott

P.S. Please visit this page to read more articles within my “ASP.NET 2.0 Tips/Tricks/Recipes and Gotchas” series.

18 Comments

  • always providing the good and useful stuff...thanks.

  • Change:

    If (Context IsNot Nothing) And (Context.User.IsInRole("Developer")) Then

    to:

    If (Context IsNot Nothing) AndAlso (Context.User.IsInRole("Developer")) Then

    to prevent a NullReferenceException if Context is null.

    - Taiwo

  • Hi Scott,

    i use the application_onerror on many websites to track the errors on the Liveserver.

    Therefor i send a Email to me, if the server is not the localhost:
    ============================

    Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)

    Dim URL As String = Request.Url.ToString.ToLower

    If URL.IndexOf("127.0.0.1") > -1 Then Exit Sub
    If URL.IndexOf("localhost") > -1 Then Exit Sub
    If CType(Server.GetLastError, HttpException).GetHttpCode = 404 Then Exit Sub

    '--catch Invalid Viewstates
    If Server.GetLastError.ToString.IndexOf("System.Web.HttpException: Invalid_Viewstate") > -1 Then
    Response.Redirect(Settings.ErrorPage)
    Exit Sub
    End If

    '--Redirect to ErrorPage
    Alerter.Alert(Server.GetLastError, Request, Session)
    Response.Redirect(Settings.ErrorPage)

    End Sub

    ============================

    regards
    Christoph

  • thanks, keep the good work going.

  • Good idea, but why put it in global.asax? That's so ASP.OLD. ;) HttpModules are so now!

  • Jeff,

    Putting it on the global.asax garatees the you'll get the error, you can always do a Server.Transfer to another page so you still got the error and the controls for debugging. If you add it in the configuration file, some times you don't get redirected and you don't want to get redirect it anyway as you don't have the stack that way.
    Al

  • Another great post! Thank you for this.

  • This is a very good trick. Thanks

  • Hi Edgar,

    You could write code within the Application_Error event above to send the error details to an administrator (you can use the System.Net.Mail namespace to-do this).

    Alternatively, ASP.NET 2.0 has a built-in "health monitoring" feature that enables you to configure the system to send an email to an adminsitrator automatically. If you do a search on ASP.NET 2.0 Health Monitoring and Email you should find some articles on how to-do this.

    Hope this helps,

    Scott

  • I&#39;ve expanded upon this approach slightly by attempting to cast the exception to type HttpException if possible.
    This allows me to use the GetHtmlErrorMessage() method for output, and the GetHttpCode() method to check for simple 404s.
    Response.Write(HttpException.GetHtmlErrorMessage());
    displays the standard error handler message with the source snippet and everything, which is almost optimal.

  • Hi Nathaneal,

    Good suggestion!

    Thanks for sharing,

    Scott

  • Scott, how can one catch a 500 Internal Server Error when you have a | character in the URL? It gets through the httpruntime, as you can see from the generated response, but the statement that Application_Error will execute for sure sooner or later is broked. It sais Illegal character in path, but I can't find a way to intercept it. I have tried even the msdn2 site and it seems they don't catch it either.

    This is only for my (and maybe others') curiosity, as it's rare to get such requests.

  • Hi Adi,

    I believe your Application_Error event will fire in this case. Note that if you don't call Server.ClearError() within this event, though, the error will continue and cause the error message to be displayed.

    Thanks,

    Scott

  • Scott you're right. I assumed it was an HttpException with the http code of 500, but it's just an ArgumentException, which results in a responseof 500 Internal Server Error. So the msdn guys, and a few other sites were right not handling it. I think I should stick with the case when there's an actual HttpException and leave this one as is.

    Thanks for your response,
    Adi

  • We've got something like the following in Application_Error, but it's producing white screens on 404's:

    System.Exception ex = Server.GetLastError().GetBaseException();

    Server.ClearError();

    Response.Clear();

    Server.Transfer("exception.aspx", true)

    Debugging shows that the exception page is executing normally, assigning error details to it's label normally, etc. Normally this gives us the desired custom error screen, but on 404's (and other parser level errors) we're just getting a blank page. Is IIS and Asp.Net doing something after the fact?

    Thanks,

    Will

  • Forgot to mention, after we capture the error, we throw it into Context...

    System.Exception ex = Server.GetLastError().GetBaseException();

    Server.ClearError();

    Context.Items["except"] = ex;

    Response.Clear();

    Server.Transfer("exception.aspx", true)


    ;)

    Thanks,
    Will

  • Are you trying to do anything fancy in the 404 page, or is it a straight-up pretty-looking page with no backend functionality?

  • If you are calling a web service from javascript, and you want to ensure that your exceptions make it back the browser properly, you must set in your web.config. For some reason, if you turn this to "On" or "RemoteOnly" your exception message will change to "Could not process the request" for any exception returned to the browser (which is not very helpful). You can then progmatically handle your errors by implementing Application_Error() in the Global.asax file, as Scott shows in his post.

Comments have been disabled for this content.