Exception Handling in ASP.NET Web Application
In web application development, we should always think about returning some kind of message to users when an exception is raised in the program. Sometimes, it can be very specific, while in other cases it can be generic. A common thought is to capture and possibly handle exceptions where they are raised, re-throw exceptions or raise custom exceptions as needed so they can propagate up to higher level tiers in the application, handle/log exceptions at a central place in the highest tier of the application, and finally display an error message to the end-users or redirect them to a customized error page.
This pattern works, but sometimes it is tricky to generate a meaningful message. The problem is that the exception can be thrown anywhere in your application, and the exception message may not always be appropriate or meaningful to end-users. If you blindly display all exception messages you finally get, users may see something like “Object reference not set to an instance of an object” which doesn’t mean much to them. So, in the final step of exception handling (where the program displays error messages or redirect users to an error page), you will see all kinds of exceptions which include .NET originated exceptions, exceptions raised by the program on purpose, and custom exceptions. Some of them are appropriate to show to users, while others aren’t. So, the question is how you can distinguish among them.
A lot of people suggest that if the original .NET exception message isn’t meaningful to users, and you can’t provide more specific message either, then a generic message such as “An error has occurred… Please contact the technical support team for help” can be used. I agree too. You may do this where you know an exception can be thrown, and re-throw the exception with customized message (whether specific or generic) and with the original exception embedded as the inner exception of the new exception. However, you don’t want to just put try/catch blocks in every piece of your code, and catch and re-throw every exception the program may possibly encounter. It’s labor intensive and inefficient. Also, there will always be something you can’t think of or look over, so there are still chances you can get unexpected exceptions in the end.
So, my solution is to let the program only catch and re-throw .NET originated exceptions when they are predictable and additional meaningful information can be provided, and let all other exceptions bubble up to the top tier. Then in the final step of exception handling, identify the exceptions that are re-thrown, and retrieve the error message directly from these exceptions; use a generic error message for all other exceptions. The question is how to identify these exceptions that are re-thrown. For this purpose, I usually create a custom exception class which is inherited from System.Exception. Use this class whenever I need to throw new exceptions or re-throw .NET originated exceptions. So, in the final step, I check the type of the exception, and decide whether to use it as the error message or use a generic one instead.
Here is the custom exception class:
- public class ExceptionBase : Exception
- {
- public ExceptionBase(string msg, Exception innerEx)
- : base(msg, innerEx)
- {
- }
- public ExceptionBase(string msg)
- : base(msg)
- {
- }
- }
The ExceptionBase class has two constructors which give you the options to create an instance of exception with customized error message and with/without inner exception.
Here is how to use it in code:
- throw new ExceptionBase(“Error has occurred in accessing database.”);
Or
- try
- {
- // Data access operations
- }
- catch (Exception ex)
- {
- throw new ExceptionBase(“Error has occurred in accessing database.”, ex);
- }
The central location I handle all exceptions is in global.asax, and here is what it looks like:
- protected void Application_Error(object sender, EventArgs e)
- {
- Exception ex = Server.GetLastError();
- string msg = “Unexpected error has occurred.”;
- if (ex != null)
- {
- LoggingUtility.LogException(ex);
- // Remove the HttpUnhandledException wrapper if there is one
- if (ex.GetType() == typeof(HttpUnhandledException) && ex.InnerException != null)
- {
- ex = ex.InnerException;
- }
- // If this is a custom exception, change the message.
- if (ex is ExceptionBase)
- msg = ex.Message;
- Server.ClearError();
- }
- // Try to redirect user.
- Response.Redirect(string.Format(“~/Error.htm?aspxerrorpath={0}&error={1}”, HttpUtility.UrlEncodeUnicode(Request.Url.PathAndQuery), msg), true);
- }
One note for this is that ASP.NET may put a wrapper exception (HttpUnhandledException) around the original exception. In the code above, it is simply removed because it doesn’t contain any useful information.
You can also create more custom exception classes based on the ExceptionBase class to improve consistency in your application, and the code above will still work. One example is:
- public class BadUrlException : ExceptionBase
- {
- public BadUrlException(Exception innerEx)
- : base(“Bad URL has been detected. Request could not be processed.”, innerEx)
- {
- }
- }
This way the error messages for one type of exception are consistent across the application.
Tags: ASP.NET, Exception Handling