System web httpapplication error

User-215451226 posted
  • Remove From My Forums
  • Question

  • User-215451226 posted

    Hello All.
    A Very Happy Christmas in advance.

    I need some help with this weird scenario. It is mighty confusing to me and I am not clear on how to resolve the issue.
    Actually, I have a solution which includes 8 projects, 7 of which are class library and the main one is MVC4. I’m on VS2013.

    There is a class-library project called ECartSystems.System.Diverse which has a class called ‘SearchAttributes’ which is basically for building up search-condition for searching the database before using it in the calls to the stored-procedures to
    query the database and fetch/show data on the web pages.

    Now my problem is, as soon as I add reference of this class library project to my main MVC4 project ECartSystems.UI, the Global.asax.cs file gets corrupted and it says

    ‘The type or namespace name ‘Web’ does not exist in the namespace ‘ECartSystems.System’ (are you missing an assembly reference?) D:ECartSystemsECartSystemsECartSystems.UIGlobal.asax.cs 18 41 ECartSystems.UI

    When I open the Global.asax.cs file, I see that the System.Web.HttpApplication portion is showing the red error wibbly.

    What is the problem here? Why did this error appear suddenly? Where is the twist? It had never appeared before. How can this be resolved the easiest way? Please help me identify the problem and rectify it. Please don’t hesitate to ask for any other information
    related to this difficulty I am facing. I’d be happy to furnish them, so that it’s easier for you. Any kind of assistance will be good for me.

    Thanking you all.
    I will eagerly await all your replies.

Answers

  • User-760709272 posted

    public class MvcApplication: global::System.Web.HttpApplication
    • Marked as answer by

      Thursday, October 7, 2021 12:00 AM

  • User-760709272 posted

    The compiler needs to know the full class name of any types you use, which is the namespace and the type name, so System.Web.HttpContext and so on.  But Visual Studio will use the «using» statements to make a best guess at your full type names from
    entering a partial name.  Before when you used System.Web visual studio knew that System.Web had to be the system.web namespace.  When you added a reference to the third party dll that contains a namespace called ECartSystems.System and use System.Web,
    Visual Studio thinks you mean

    ECartSystems.System.Web

    so it looks in that assembly and can’t find your classes.  Well there is an even fuller way to describe a class that just it’s full namespace and class, and that is by using the alias of the assembly too.  Click on a reference in VS and look at
    the properties and you’ll see the reference has an aliases and this defaults to global.  If you need the same namespace in different assemblies, you give each assembly a different alias and use

    alias1::namespace

    alias2::namespace

    The trick to the global thing is that when you use

    global::System.Web

    Visual Studio knows the namespace has to start with system.web so it knows ECartSystems.System.Web isn’t the namespace you’re talking about.

    It’s like the file system, you can use relative references like «folderfile» or «….folderfile» then the app needs to do some work to work out what folder you’re talking about.  When you add the qualifying drive letter like «c:windows» then there
    is no ambiguity, the operating system knows exactly the folder you are trying to reference.

    • Marked as answer by
      Anonymous
      Thursday, October 7, 2021 12:00 AM

Error handling is an important part of any application. ASP.NET provides several different ways to handle errors. In this article, we will review MVC error handling best practices.

Between .NET, ASP.NET, and MVC there are several potential ways to handle application errors.

  • Web.Config customErrors
  • MVC HandleErrorAttribute
  • Controller.OnException method
  • HttpApplication Application_Error event
  • Collect exceptions via .NET profiling with Retrace

There are some pros and cons to all of these ways to handle errors. You probably need to use a combination of them to properly handle and log errors.

There are two critical things that you need accomplish with error handling:

  1. Gracefully handle errors and show your users a friendly error page.
  2. Logging errors so that you are aware of them and can monitor them.

Must Have: Global Error Page With Web.Config <customErrors>

The last thing you ever want your users to see is a “yellow screen of death” type error. If you don’t know what that is, I’m referring the standard yellow ASP.NET error screen.

For any application, I would always recommend specifying a custom error page in your Web.Config. Worst case scenario, your users will see this page if an unhandled exception occurs.

<system.web>
    <customErrors mode="On" defaultRedirect="~/ErrorHandler/Index">
        <error statusCode="404" redirect="~/ErrorHandler/NotFound"/>
    </customErrors>
<system.web/>

Use MVC HandlerErrorAttribute to Customize Responses

HandleErrorAttribute inherits from the FilterAttribute and can be applied to an entire controller or individual controller action methods.

It can only handle 500 level errors that happen within an MVC action method. It does not track exceptions that help outside of the MVC pipeline. Exceptions may occur in other HTTP modules, MVC routing, etc.

When to Use HandleErrorAttribute

Since it does not provide a way to collect all exceptions that could ever happen, it is a bad solution for a global unhandled error handler.

It works perfectly for tailoring specific error pages for a specific MVC controller or action method. Specifying an error page in your Web.Config <customErrors> works ideally for a universal error page. HandleErrorAttribute gives you fine-grained control if you need it.

Note: HandleErrorAttribute requires customErrors to be enabled in your Web.Config.

For example, if you wanted to show a particular MVC view when a SqlException happens, you can do it with the code below:

[HandleError(ExceptionType = typeof(SqlException), View = "SqlExceptionView")]
public string GetClientInfo(string username)
{
	return "true";
}

The problem HandleErrorAttribute is it doesn’t provide a way to log the exception!

Use the MVC Controller OnException to Customize Responses

OnException is similar to HandleErrorAttribute but provides more flexibility. It works with all HTTP status codes, and not just 500 level responses. It also gives you the ability to log the errors!

public class UserMvcController : Controller
{
   protected override void OnException(ExceptionContext filterContext)
   {
      filterContext.ExceptionHandled = true;

  //Log the error!!
      _Logger.Error(filterContext.Exception);

      //Redirect or return a view, but not both.
      filterContext.Result = RedirectToAction("Index", "ErrorHandler");
      // OR 
      filterContext.Result = new ViewResult
      {
         ViewName = "~/Views/ErrorHandler/Index.cshtml"
      };
   }
}

When to Use OnException for MVC Error Handling

If you want a way to present your users custom MVC views or custom log exceptions, OnException is a good solution for you. It provides more flexibility than HandleErrorAttribute and does not require customErrors to be enabled in your Web.Config file.

Note: OnException gets called for all HTTP status codes. So be careful how you handle simple issues like a 404 caused by a bad URL.

Use HttpApplication Application_Error as Global Exception Handler

So far we have covered three different ways to customize the response that your users see if an exception occurs. Only within OnException can you potentially log exceptions.

To log all unhandled exceptions that may occur within your application, you should implement a basic error logging code as shown below.

public class MvcApplication : System.Web.HttpApplication
{
   protected void Application_Start()
   {
      AreaRegistration.RegisterAllAreas();
      FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
      RouteConfig.RegisterRoutes(RouteTable.Routes);
      BundleConfig.RegisterBundles(BundleTable.Bundles);
   }

   protected void Application_Error()
   {
      var ex = Server.GetLastError();
      //log the error!
      _Logger.Error(ex);
   }
}

When to Use Application_Error

Always! HttpApplication’s Error even provides the best mechanism to collect and log all unhandled application errors.

Collect All .NET Exceptions With Stackify Retrace

Stackify’s APM solution, Retrace, taps into the .NET profiling APIs to track the performance of your app down to the code level. As part of that, it can automatically collect all unhandled exceptions or can be configured to receive all exceptions ever thrown, even if they are handled and discarded. Retrace doesn’t require any code changes either!

MVC Error Handling

Screenshot from Retrace

Summary on MVC Error Handling

There are several ways to do MVC error handling. You should always specify a default error page via your web.config <customErrors> and log unhandled exceptions that get called back to your HttpApplication Error method.

You can use HandleErrorAttribute or OnException to provide fine-grained control of how you display error type messages to your users.

Here you will learn how to handle exceptions in ASP.NET MVC application.

You may handle all possible exceptions in the action methods using try-catch blocks.
However, there can be some unhandled exceptions that you want to log and display custom error messages or custom error pages to users.

When you create an MVC application in Visual Studio, it does not implement any exception handling technique out of the box. It will display an error page when an exception occurred.

For example, consider the following action method that throws an exception.

namespace ExceptionHandlingDemo.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Contact()
        {
            string msg = null;
            ViewBag.Message = msg.Length; // this will throw an exception

            return View();
        }
}

Navigating to /home/contact in the browser, and you will see the following yellow page (also known as the Yellow Screen of Death) that shows exception details such as exception type, line number and file name where the exception occurred, and stack trace.

Default error page in MVC

Default Error Page in MVC

ASP.NET provides the following ways to handle exceptions:

  1. Using <customErrors> element in web.config
  2. Using HandleErrorAttribute
  3. Overriding Controller.OnException method
  4. Using Application_Error event of HttpApplication

<customErrors> Element in web.config

The <customErrors> element under system.web in web.config is used to configure error code to a custom page. It can be used to configure custom pages for any error code 4xx or 5xx. However, it cannot be used to log exception or perform any other action on exception.

Enable the <customErrors> in web.config, as shown below.

<system.web> 
    <customErrors mode="On"></customErrors>
</system.web> 

You also need to add HandleErrorAttribute filter in the FilterConfig.cs file.

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
    }
}

After enabling the customErrors mode to On, an ASP.NET MVC application will show the default custom error page, as shown below.

Custom Error Page

The above view is Error.cshtml in the Shared folder. It will be displayed on the 500 error code.

The HandleErrorAttribute filter set the Error.cshtml as the default view to display on an error occurred.

Learn more about handling exceptions using web.config customErrors in ASP.NET MVC.

HandleErrorAttribute

The HandleErrorAttribute is an attribute that can be used to handle exceptions thrown by an action method or a controller. You can use it to display a custom view on a specific exception occurred in an action method or in an entire controller.

The HandleErrorAttribute attribute can only be used to handle the exception with status code 500. Also, it does not provide a way to log exceptions.

In order to use this attribute, you must add HandleErrorAttribute filter in the FilterConfig.RegisterGlobalFilters() method and
also, set the mode attribute to On <customErrors mode="On"> in web.config, as we did for the customErrors section above.

Now, let’s apply [HandleError] attribute to the action method, as shown below.

public class HomeController : Controller
{
    [HandleError]
    public ActionResult Contact()
    {
        string msg = null;
        ViewBag.Message = msg.Length;
            
        return View();
    }
}

Above, we configured [HandleError] attribute on the Contact() action method.
It will display Error.cshtml view from the Shared folder when an exception occurs. The [HandleError] set the Error.cshtml view as default view for any exceptions.

the [HandleError] can also be used to configure different pages for different types of exceptions, as shown below.

public class HomeController : Controller
{
    [HandleError]
    [HandleError(ExceptionType =typeof(NullReferenceException), View ="~/Views/Error/NullReference.cshtml")]
    public ActionResult Contact()
    {
        string msg = null;
        ViewBag.Message = msg.Length;
            
        return View();
    }
}

Now, the above example will show NullReference.cshtml because it throws NullReferenceException.

The [HandleError] attribute has a limited scope and not recommended to use in most cases.

Overriding Controller.OnException Method

Another way to handle controller level exceptions is by overriding the OnException() method in the controller class. This method handles all your unhandled errors with error code 500.

It allows you to log an exception and redirect to the specific view. It does not require to enable the <customErrors> config in web.config.

public class HomeController : Controller
{
    public ActionResult Contact()
    {
        string msg = null;
        ViewBag.Message = msg.Length;
            
        return View();
    }
    
    protected override void OnException(ExceptionContext filterContext)
    {
        filterContext.ExceptionHandled = true;

        //Log the error!!
     
        //Redirect to action
        filterContext.Result = RedirectToAction("Error", "InternalError");

        // OR return specific view
        filterContext.Result = new ViewResult
        {
            ViewName = "~/Views/Error/InternalError.cshtml"
        };
   }
} 

Using Application_Error event of HttpApplication

The ideal way to log exception occurred in any part of your MVC application is to handle it in the Application_Error event in the global.asax file.

public class MvcApplication : System.Web.HttpApplication
{
    //other code removed for clarity

    protected void Application_Error()
    {
        var ex = Server.GetLastError();
        //log an exception
    }
}

The Application_Error event is fired on any type of exception and error codes. So, handle it carefully.

Recommendation

In most web applications, you should ideally log the exceptions and also show appropriate error messages or pages to the users. So, it is recommended to use the global Application_Error event to log all the exceptions along with <customErrors> element in web.config to redirect it to appropriate pages.

The above exception handling techniques will return the response with 200 status code. If you are concern to return specific error code in response then you have to use <httpErrors> element in web.config. Learn how to display a custom error page with the appropriate error code in ASP.NET.

By Adam Tuliper

All applications should have error handling. This we all know. We can’t always be notified of an unhandled error (and usually aren’t) when one occurs on a client’s machine. The advantage we have on the Web is that we can always be notified when an unhandled error occurs. With the advent of ASP.NET,
there are some great new ways to handle errors. There are some differences in .NET in not only how to handle the error, but how the information is provided to you. For example, classic ASP uses Server.GetLastError to return an ASPError object. You can and should still use Server.GetLastError in .NET, but this now returns a type System.Exception. I must give Microsoft credit for making almost everything consistent in .NET, which is quite a welcome change.

The Problem


Errors will occur in our applications. We try to trap for most errors using try-catch blocks (or the only possibility in tradional ASP ‘on error resume next’); however, we usually don’t cover every possible exception. What happens when an unhandled error occurs? Usually the user is brought to IIS’s default error pages (usually located in c:winnthelpiishelpcommon). The downsides are you have no idea when this occurs and the page doesn’t have your site’s look and feel. Errors are a development fact, but we strive to eliminate or handle them gracefully. With this in mind, we need to know:

  1. When an error occurs
  2. Where it occurred
  3. What the error is

Having a central location such as the event log, database or some other log file to log errors is essential for debugging this problem later (I call this forensics debugging).

IIS provides great error-handling capabilities. There are some problems with these though. Sometimes we know an error will occur, and we can’t always trap for it in a nice way without overriding the site’s (done in the IIS custom errors
Page; see the article mentioned above) default error redirection page. For example, upon access to a resource that requires authentication, we may need to redirect to an application’s login page. Also, a very common problem exists with Web hosts. If you have a hosted Web site, you usually have no control over its IIS configuration. Thus, setting up custom error pages can be next to impossible in traditional ASP. This is elimiated with ASP.NET, as you will learn as you read on.

The Solution


For such a list of problems, the solution is actually pretty simple. There are three places in ASP.NET to define what happens to these unhandled errors.

  1. In the web.config file’s customErrors section.
  2. In the global.asax file’s Application_Error sub.
  3. On the aspx or associated codebehind page in the Page_Error sub.

The actual order of error handling events is as follows:

  1. On the Page itself, in the Page_Error sub (this is default, you can name it anything because it specificed Handles MyBase.Error)
  2. The global.asax Application_Error sub
  3. The web.config file

Note: To cancel the bubbling up of the error at anytime for the Page_Error or Application_Error, call the “Server.ClearError” function in your sub.
Each method has its own uses, as I will explain.

When an exception occurs in your application, it should be an object inherited from type System.Exception, and as such will have the following public members:

HelpLink Gets or sets a link to the help file associated with this exception.
InnerException Gets the Exception instance that caused the current exception.
Message Gets a message that describes the current exception.
Source Gets or sets the name of the application or the object that causes the error.
StackTrace Gets a string representation of the frames on the call stack at the time the current exception was thrown.
TargetSite Gets the method that throws the current exception.

Using the Page_Error or OnError sub


The first line of defense in error handling happens at the page level. You can override the MyBase.Error sub as such: (Visual Studio will complete the code if you click either the Overrides or BaseClass events in the editor). The two functions you can use (one or the other, both will not work as only one will get called)



Private Sub Page_Error(ByVal sender As Object, ByVal e As System.EventArgs)
Handles MyBase.Error

End Sub

Or you can use this one:



Protected Overrides Sub OnError(ByVal e As System.EventArgs)

End Sub

Handling errors in these subs is a simple process. Just call Server.GetLastError to return the error. If you want to redirect to a specific page here
you can just call Response.Redirect (“HandleError.aspx”) or whatever your page may be. This method of handling errors is good for several reasons.

  1. If you need to override the Application_Error or the customErrors setup in the web.config file
  2. If each page must implement it’s own error handling
    If you need to log specific information and then carry on, just code for your logging or whatever here, and that is all. If you need to cancel
    the error processing here (so it doesn’t go to the Application_Error or customErrors) simply call Server.ClearError in this sub.

Using the global.asax File


The global.asax file contains the next line of defense against errors. When an error occurs, the Application_Error sub is called. This location happens
to be my favorite place to log errors because it is the most functional. For most of my applications in .NET, I don’t handle too many custom errors at the
page level. I handle them at the application level. The only two locations that actually give you access to Server.GetLastError are in the Page_Error and Application_Error subs.

After the Page_Error is called, the Application_Error sub is called. Here you can also log the error and redirect to another page. I won’t explain anything else about it because it is basically the same as the Page_Error but happens to be at the application level rather than the page level.

Using the web.config File


The customErrors element of the web.config file is the last line of defense against an unhandled error. If you have other error handlers in place,
like the Application_Error of Page_Error subs, these will get called first. Provided they don’t do a Response.Redirect or a Server.ClearError, you
should be brought to the page(s) defined in the web.config. In the web.config file, you can handle specific error codes (500, 404, etc), or you can use one
page to handle all errors. This is a major difference between this method and the others (although you can emulate this by doing various Response.Redirects
using the other methods). Open up your web.config file. The customErrors section uses this format:



<customErrors defaultRedirect=”url” mode=”On|Off|RemoteOnly”>
<error statusCode=”statuscode” redirect=”url”/>
</customErrors>

Here is some important information about the “mode” attribute:

“On” specifies that custom errors are enabled. If no defaultRedirect is specified, users see a generic error.

“Off” specifies that custom errors are disabled. This allows display of detailed errors.

“RemoteOnly” specifies that custom errors are shown only to remote clients, and ASP.NET errors are shown to the local host. This is the default.

By default, the section looks like this when you create a Web application.



<customErrors mode=”RemoteOnly” />

This will show a generic page to users. To redirect it to one of your own pages, you would change it to this:



<customErrors mode=”On” defaultRedirect=”error.htm” />

Now all errors that occur will be brought to the error.htm page.

To handle specific errors, and redirect to the error page for everything else you can specify the error code you want specially handled like so:



<customErrors mode=”On” defaultRedirect=”error.htm”>
<error statusCode=”500″ redirect=”error500.aspx?code=500″/>

<error statusCode=”404″ redirect=”filenotfound.aspx”/>
<error statusCode=”403″ redirect=”authorizationfailed.aspx”/>
</customErrors>

There is a problem here with this solution. Once the redirect is done, your error information is no longer available on the redirected page.
This is because IIS (via the .net framework) performs a plain old GET request to the error page and does not do a “Server.Transfer” like the built-in IIS error handling does.

The only information available to you at this time is the URL that caused this error to be raised. This is located on the querystring as “aspxerrorpath”:
http://localhost/ErrorHandling/error500.aspx?aspxerrorpath=/ErrorHandling/WebForm1.aspx. The only places this information is available is the two methods described above.

Another interesting point about the above customErrors element is that you can specify different error pages for different subdirectories.

For this example, let’s say you have a directory named “Customers” off of your root directory that contains a branding specific for logged in customers
but is not in its own application. As such you want to define a different set of pages for errors. Please note that these pages specified in the “redirect” attribute are relative to the “Customers” subdirectory and not the root path of your site. I have also included a security rule which says only MYDOMAINCustomers can access these files. You can define rules for these errors in the web.config file:



<configuration>
<system.web>


</system.web>

<!– Configuration for the “Customers” subdirectory. –>
<location path=”Customers”>

<system.web>
<customErrors mode=”On” defaultRedirect=”error.htm”>
<error statusCode=”500″ redirect=”CustomerError500.aspx”/>
<error statusCode=”401″
redirect=”CustomerAccessDenied.aspx”/>
<error statusCode=”404″
redirect=”CustomerPageNotFound.htm”/>
<error statusCode=”403″ redirect=”noaccessallowed.htm”/>

</customErrors>
<authorization>
<allow roles=”MYDOMAINCustomers” />
<deny users=”*” />
</authorization>
</system.web>

</location>

Note: One thing I found in development is there seems to be an inheritance order for these errors. What I mean by this is
if you have a 500 error defined for the root site, but none defined for the customers directory, but you DO have a defaultRedirect
set for the customer directory, the 500 handler defined at the root level will be called. So if a parent directory has a handler.

Using the Code


I have created an application with settings, so you can get an idea of how to configure your code. In the zip file there is a solution containing two projects.

The first is a Web project that has some buttons to cause different errors. It also shows an example of handling the error through the page, global.asax, and web.config file. There will also be a DotNetErrorLog.sql you can run in query analyzer to create a database (and user) to start logging errors ASAP.

You will notice in my web.config I have the following:



<appSettings>
<add key=”ErrorLoggingLogToDB” value=”True” />
<add key=”ErrorLoggingLogToEventLog” value=”True” />
<add key=”ErrorLoggingLogToFile” value=”True” />

<add key=”ErrorLoggingConnectString” value=”Initial
Catalog=DotNetErrorLog;Data Source=localhost;Integrated Security=SSPI;” />
<add key=”ErrorLoggingEventLogType” value=”Application” />
<add key=”ErrorLoggingLogFile” value=”c:ErrorManager.log” />
</appSettings>

This is where I keep specific settings for an application. You do not have to worry about keeping it in the registry, and this is great for moving your applications between development, integration, and production environments (if you are blessed with that). For better security, you can incorporate the encryption classes in .NET to encrypt the database connection info and store that information in the web.config rather than the plain text connectstring, but that obviously isn’t the purpose of this article. The settings pretty much are as follows:

  1. To log to a db:

    1. ErrorLoggingLogToDB – Set to “True” to tell the app you want to log info into the db
    2. ErrorLoggingConnectString – The connect string to connect to the database to store errors

  2. To log to the event log:

    1. ErrorLoggingLogToEventLog – Set to “True” to tell the app you want to log error information to the event log
    2. ErrorLoggingEventLogType – The name of the event log to log to (ex. System, Application, etc etc.). You can even create your own log just for web errors too, which could be ideal for large sites!

  3. To log to a text file:

    1. ErrorLoggingLogToFile – Set to “True” to tell the app you want to log info to a text file
    2. ErrorLoggingLogFile – The path of the file to log errors to

Here is a sample of what to expect in the log file or the event log:


—————–12/20/2002 3:00:36 PM—————–

SessionID:qwyvaojenw1ad1553ftnesmq

Form Data:

__VIEWSTATE – dDwtNTMwNzcxMzI0Ozs+4QI35VkUBmX1qfHHH8i25a/4g4A=

Button1 – Cause a generic error in the customer directory

1: Error Description:Exception of type System.Web.HttpUnhandledException was thrown.

1: Source:System.Web

1: Stack Trace: at System.Web.UI.Page.HandleError(Exception e)

1: at System.Web.UI.Page.ProcessRequestMain()

1: at System.Web.UI.Page.ProcessRequest()

1: at System.Web.UI.Page.ProcessRequest(HttpContext context)

1: at System.Web.CallHandlerExecutionStep.Execute()

1: at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

1: Target Site:Boolean HandleError(System.Exception)

2: Error Description:Object reference not set to an instance of an object.

2: Source:ErrorHandling

2: Stack Trace: at ErrorHandling.WebForm2.Button1_Click(Object sender, EventArgs e) in C:InetpubwwwrootErrorHandlingCustomersWebForm2.aspx.vb:line 26

2: at System.Web.UI.WebControls.Button.OnClick(EventArgs e)

2: at System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument)

2: at System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument)

2: at System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData)

2: at System.Web.UI.Page.ProcessRequestMain()

2: Target Site:Void Button1_Click(System.Object, System.EventArgs)

First, the date and time is logged. Secondly, the user’s session id is logged. These session ids look quite different than classic asp session ids which were all numeric.
The next line contains any form data on the page. This is great especially if they filled in information on the page, and that information caused your app to bomb. All of
the lines with “1” in front of it are the first error. This contains the description, source, stack track and what function caused the error. Starting at the “2”s, this
is the error generated before #1. Error #2 here is the “InnerException” to error one. This is a new idea here (from classic asp or vb) since it allows error information
to have a hierarchy to it. Some errors can be trapped, and rethrown with a new error giving more specific information.

Another idea is to use the SMTP component to e-mail you when an error occurs, enabling you to be proactive for errors.
This would be a simple addition to the appSettings section above to hold an e-mail address and simply use the CErrorLog.GetErrorAsString to get the text needed to send an e-mail out.

Special Notes


– I read somewhere on the Net that someone recommended taking the error number and then looking up the proper message in a database to display to the user.

I like this idea; however, the downside is if an unexpected database error occurs, then you have just lost your errorpage ability.

– A quick note about performing a redirection inside a try-catch block:

If you want to redirect to a page within a given try-catch block in your code (custom error handlers included), understand this can fail under certain circumstances because a Response.Redirect calls Response.End within its internal code, creating problems. You must call Response.Redirect(“pagename.aspx”,False”)
which specifies the redirect call and will not call Response.End, thus, preventing the exception.

Good luck and happy error logging!

About the Author

Adam Tuliper’s professional career involves developing .Net, COM, VB, ASP, C, C++, and SQL Server solutions for business solutions and consumer applications.
He’s been developing for over six years professionaly. He is also the owner of gecko software, where they develop Internet center software, system monitoring, and security scanning software. He has a degree in CIS. He also serves as a security consultant to several companies.

In the previous post I covered the Custom Errors and HTTP Errors error handling features. You can configure both of these in various ways, including specifying that your own error page should be shown when there is an error. What you do not get is complete control. Today you are going to see how you can gain that control by manually handling errors. As with the last post, I am continuing to concentrate on how 500 Internal Server Error errors can be handled.

This is Part 3 of a series of posts on the subject of adding error handling to an ASP.NET MVC 3 Web application. The index to this series can be found here.

The Basics of Manually Handling Errors

In an ASP.NET MVC Web application, there are two basic approaches to manually handling errors: the HandleErrorAttribute filter and the HttpApplication.Error event. With both of these, there are a number of desired behaviours that are important for the resulting response to be correct.

  • A non-404 error should be logged.
  • No redirect should occur.
  • The response needs to be cleared to remove any headers and/or content that have already been written to the response before the exception was thrown. (As will be discussed later in the post, there are actually situations where clearing the response is unnecessary because the error page is rendered in a new request.)
  • The response status code needs to be set to the appropriate error code.
  • TrySkipIisCustomErrors needs to be set to prevent IIS replacing your error response with its own.
  • The exception that triggered the error needs to be marked as handled, so that ASP.NET does not pass it on to the next error handling feature.

Exactly how these steps are performed depends on the way you are manually handling errors.

Manual Error Handling using the HandleErrorAttribute Filter

Action filters are the mechanism used in ASP.NET MVC for declaratively adding additional behaviour to an action method. Action filters are implemented as attributes, and these attributes can be added to an action method, to a controller, or to all controllers in the site. There are four types of action filter, but the one of interest here is the exception filter type. It is invoked when an unhandled exception is thrown from within the MVC framework, either from an action method or from any action filters that get invoked during the handling of the request.

The ASP.NET MVC framework comes with an exception filter called HandleErrorAttribute. If you create an MVC 3 Web application and then look at the site’s Global.asax.cs file, you will see that an instance of this exception filter gets added to the global filter collection at application start-up:

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute());
}

By adding the filter to the global filter collection, it gets applied to all controllers and therefore all action methods. Note that earlier version of ASP.NET MVC do not have this global filter collection; you have to individually apply the filter to the controllers in your Web site.

While this filter is the exception filter that is applied by default, there is nothing to stop you creating your own exception filter for the site to use instead. If you need to add additional behaviour to the HandleErrorAttribute class, you can just derive from it and override the OnException method. (Deriving is something I have done in the demo application because I needed to be able to switch the HandleErrorAttribute filter on and off.)

The HandleErrorAttribute filter can be customized in two useful ways. Firstly, you can specify what type of exceptions it should handle. By default this type is System.Exception, so it potentially handles all types of exception. Secondly, you can specify the view it should render as the response. The following is an example of how to specify these customizations:

new HandleErrorAttribute
{
    View = "SomeView",
    ExceptionType = typeof (SomeException)
}

Notice that you specify a view rather than an action method. By default it renders a view called “Error” – this view gets automatically added when you create an MVC Web site – but in the demo application I have instead specified that it should render a view called “HandleErrorAttributeMvcErrorPage”. This view can be found in the /Views/Shared directory.

You can go to the MVC section of the ASP.NET Codeplex Web site and view the source code for the HandleErrorAttribute class to see how it works. The bulk of the logic is in the OnException method.

  1. If Custom Errors is not enabled, the method exits without taking any action.
  2. If the exception thrown is an HttpException and invoking GetStatusCode() on it does not return 500, the method exits without taking any action.
  3. If the type of the exception thrown is not the type that this filter should handle, the method exits without taking any action.
  4. The exception is used to populate a view model, along with the controller and action names from the original request.
  5. A view result is created using the data in the view model, one that will display the view the filter has been specified to show.
  6. The exception is marked as handled, the response is cleared, and the response status code is set to 500.
  7. The TrySkipIisCustomErrors property on the response is set to true.
  8. The created view result is returned so that it can be executed by the MVC framework.

By only handling HttpExceptions that have an HTTP status code of 500, the HandleErrorAttribute allows errors like 404 Not Found errors to remain unhandled, and so be dealt with in some other way.

Looking back at the previous section at the list of desired behaviours when manually handling errors, you can see that the HandleErrorAttribute takes care of all of the behaviours except the logging of the exception. You could add this by deriving from the HandleErrorAttribute class and overriding the OnException method. In that method you would call the base OnException method and then log the exception if that base method marked the exception as handled:

public class DerivedHandleErrorAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        base.OnException(filterContext);
        
        if (context.ExceptionHandled)
        {
            // Log filterContext.Exception in some way.
        }
    }
}

This is the approach described by Atif Aziz on Stack Overflow. An alternative approach is that taken by Ivan Zlatev, where you create an exception filter that logs the exception if it has been marked as handled. Like the HandleErrorAttribute filter, this logging filter is also registered in global.asax as a global filter, but it is registered first. The exception filters get invoked in reverse order of their registration, so the logging filter gets invoked after the HandleErrorAttribute filter has had a chance to mark the exception as handled.

The next experiment shows the HandleErrorAttribute filter in action.

Experiment: HandleErrorAttribute with Custom Errors on
Custom Errors – Mode On
Manual Error Handling – 500 error handling Handle Error Attribute
All other settings Default values
Error link to click Action method exception on GET request

You should get the following custom error page:

37569444-hea-with-custom-errors-on

The page has the correct status code and there is no redirect.

If you look back at the filter’s implementation details, you will see that Custom Errors needs to be enabled for it to handle the exception. So you could try turning Custom Errors off.

Experiment: HandleErrorAttribute with Custom Errors off
Custom Errors – Mode Off
Manual Error Handling – 500 error handling Handle Error Attribute
All other settings Default values
Error link to click Action method exception on GET request

You should get the following Custom Errors detailed YSOD:

37569584-hea-with-custom-errors-off

The HandleErrorAttribute checks whether Custom Errors is on or off by calling the HttpContext.IsCustomErrorEnabled method. That method determines if Custom Errors is enabled or not from the current Mode setting of Custom Errors and whether the request being handled is local or remote. In this way, if Custom Errors is in its default Mode of RemoteOnly, you get the user-friendly custom error page for remote requests and the developer-friendly YSOD for local requests.

The ASP.NET in ASP.NET MVC

As described in the first post in this series, the ASP.NET MVC framework code is only one component in the IIS request-handling pipeline for a site, given that the pipeline consists of various managed ASP.NET and native modules. This means that it is not only ASP.NET MVC code that executes to handle a given request. Given that the HandleErrorAttribute filter can only handle exceptions throw from within that framework, it is clear that such a filter can never be the only error handling solution in an MVC Web site.

The following experiment illustrates this point by asking you to click the BeginRequest exception error link. The link causes an exception to be thrown from the BeginRequest event.

Experiment: HandleErrorAttribute with an external exception
Custom Errors – Mode On
Manual Error Handling – 500 error handling Handle Error Attribute
All other settings Default values
Error link to click BeginRequest exception

You should get the basic YSOD screen. The exception was thrown from outside the MVC framework so it was not handled by the HandleErrorAttribute filter. Instead it was handled by Custom Errors.

Clearing the Response

You can do a final experiment with this filter to see a shortcoming it has. This experiment will use a different error link.

Experiment: Clearing of the response
Custom Errors – Mode On
Manual Error Handling – 500 error handling Handle Error Attribute
All other settings Default values
Error link to click Failed file download

The error screen that you’ll see will depend on the browser you are using. In Chrome I get a blank screen. In Firefox I get a message about how the image “… cannot be displayed because it contains errors”. In IE9 I get the error page displayed as text rather than be rendered as HTML.

The problem with the response is that the type of the content returned does not match the declared Content-Type in the header. The content is a Web page and a Web page should have a Content-Type value of ‘text/html’, but the response has a value of ‘image/jpeg’. You can see this for yourself if you use your browser’s developer tools to view the response headers.

The error link simulates what happens when an exception is thrown during a request for a file that is not a Web page. If you look at the FailedFileDownload action method in the ErrorGenerationController class, you will see that it returns a FileStreamResult with a stream that throws an exception as soon as the MVC framework tries to read from it. The key is that by then the MVC framework has already set the Content-Type header of the response to ‘image/jpeg’.

If you look back at the implementation details of the HandleErrorAttribute filter, you will see that it clears the response. It does this like so:

filterContext.HttpContext.Response.Clear();

You would think that the Clear method clears both the headers and the content of the response, but in fact it only clears the content. To get the error page displaying correctly, you have to do the following:

filterContext.HttpContext.Response.ClearHeaders();
filterContext.HttpContext.Response.ClearContent();

Unfortunately you cannot fix this by deriving a class from the HandleErrorAttribute filter. You could instead duplicate that filter class and fix the issue in the copy.

The HandleErrorAttribute Filter in Summary

As it is the error handling mechanism that comes with the ASP.NET MVC framework, the HandleErrorAttribute exception filter is the standard way to deal with unhandled exceptions and HttpExceptions with a 500 status code in an ASP.NET MVC Web application. It will display a friendly MVC error page that has the correct status code and without redirecting to that page. However, out-of-the-box the filter does not log the errors it handles and it does not clear the response headers. You can easily fix the logging issue, but you would have to take a different approach to fix the response-clearing issue. Furthermore, the HandleErrorAttribute exception filter can never be the only error handling mechanism in an MVC Web site since the filter only deals with particular exceptions that have been thrown from within the action method invoker code in the MVC framework.

Manual Error Handling using the HttpApplication.Error Event

As described in the first post in this series, the reqeust-handling pipeline for a given Web site includes a list of events that are raised in turn as a request is processed. You can hook into these events by adding handlers to the site’s HttpApplication class, or by creating an HTTP module and adding it to the pipeline using the site’s Web.config file. The event of interest here is the Error event, which is invoked if an unhandled exception gets thrown. You can hook into this event by adding a listener using the += operator, or by adding a method to the HttpApplication-derived class that has the following signature:

public void Application_Error(object sender, EventArgs e)

This method will get automagically added as a listener to the Error event.

It is likely that the listener you add to the Error event is one that could be reused across multiple Web sites. To facilitate this, you can create an HttpModule that registers one of its methods as a listener to that event. Karl Seguin has a post that describes this approach and its benefits. I decided to take this approach for the demo application. The HttpModule I created is called ErrorHandlerHttpModule, and you will find it in the MvcHelpers directory. The following is the skeleton of that class:

public class ErrorHandlerHttpModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        context.Error += Application_Error;
    }
    
    private static void Application_Error(object sender, EventArgs e)
    {
        // Error handling code goes here.
    } 
    
    public void Dispose() 
    {
    }
}

To add this module to the ASP.NET pipeline, I added the following markup to the demo application’s Web.config file:

<system.web>
    <!-- Additional configuration elided. -->
    <httpModules>
        <add
            name="ErrorHandlerHttpModule"
            type="MvcErrorHandling.MvcHelpers.ErrorHandlerHttpModule, MvcErrorHandling" />
    </httpModules>
</system.web>
 
<system.webServer>
    <!-- Additional configuration elided. -->
    <modules runAllManagedModulesForAllRequests="true">
        <add
            name="ErrorHandlerHttpModule"
            type="MvcErrorHandling.MvcHelpers.ErrorHandlerHttpModule, MvcErrorHandling"/>
    </modules>
</system.webServer>

Notice how the module has to be added in two separate places. The system.web addition is for IIS 6 and the Visual Studio Development Server, and the system.webServer addition is for IIS 7. This allows you to run the demo application either through IIS 7 or through the Visual Studio Development Server.

The module contains quite a lot of code that is required by the demo application to make it configurable. Ignoring that code, you will see that I start by obtaining the exception that was thrown:

var exception = HttpContext.Current.Server.GetLastError();

I then try to cast that exception as an HttpException. If the cast succeeds, I read its HTTP status code value:

var httpException = exception as HttpException;
...
var statusCode = httpException.GetHttpCode();

This status code is used for the response status code. If the exception is not an HttpException then I consider it an unhandled exception with a status code of 500.

I now want to show an MVC custom error page. This is not straightforward to do, because this code is outside of the MVC framework and I need a way to get back into it. There are at least four ways to do this: the view result approach, the controller execute approach, the MvcHttpHandler approach, and the TransferRequest approach. The view result approach only requires an MVC view, while the others require an action method and a view. I will describe each of these approaches in a moment.

For all but the TransferRequest approach, I have to then clear the error:

HttpContext.Current.Server.ClearError();

This step is important as if I did not clear the error it would remain as unhandled and would get passed to Custom Errors. My custom error page would then get overridden by whatever Custom Errors is configured to show.

The View Result Approach

The following experiment shows you this approach in action.

Experiment: The View Result Approach
Manual Error Handling – 500 error handling Application Error Handler
Manual Error Handling – App error handler response View Result
All other settings Default values
Error link to click Action method exception on GET request

You should get the following custom error page:

37570697-httpapperror-viewresult

It has the correct 500 error response code and there is no redirect. The code to create this response can be found in the Application_Error method in the ErrorHandlerHttpModule class. If you were using this approach, you could add your logging code to that method.

With this approach, I begin creating the view result response by clearing the response and setting its status code:

HttpContext.Current.Response.ClearHeaders();
HttpContext.Current.Response.ClearContent();
HttpContext.Current.Response.StatusCode = statusCode;

Now I want to create and execute a ViewResult instance, but to do that I need a controller context. And to create that controller context I need a controller instance and a RouteData object that has a controller name added to it. It turns out that, while these are required, they do not actually get used so I can use a fake controller and a ‘whatever’ controller name:

var routeData = new RouteData();
routeData.Values.Add("controller", ErrorControllerRouteName);
 
var controllerContext = new ControllerContext(
    new HttpContextWrapper(HttpContext.Current),
    routeData,
    new FakeController());

This technique of using a fake controller comes from the NotFound MVC project, which I will be discussing in the next post in the series.

Now I can create a ViewResult instance that is initialized to display the desired view. Any values required by that view can be added to the ViewResult’s ViewBag:

var viewResult = new ViewResult {ViewName = "ManualErrors"};
// Setting of values on its ViewBag here.
viewResult.ExecuteResult(controllerContext);
HttpContext.Current.Server.ClearError();

This approach is similar to how the HandleErrorAttribute filter works, since a ViewResult is executed directly rather than an action method being invoked. However, it feels like a bit of a hack to me.

The Controller Execute Approach

The following experiment shows you this approach in action.

Experiment: The Controller Execute Approach
Manual Error Handling – 500 error handling Application Error Handler
Manual Error Handling – App error handler response Controller Execute
All other settings Default values
Error link to click Action method exception on GET request

You should get the same custom error page as in the previous experiment, also with a 500 error status code and no redirect.

This approach requires that you create an action method for the error page you want to display. To execute this action, you need a RouteData instance that specifies it. If you look at the Application_Error method in the ErrorHandlerHttpModule class, you can see how I do this:

var routeData = new RouteData();
routeData.Values.Add("controller", ErrorControllerRouteName);
routeData.Values.Add("action", actionName);
 
var requestContext = new RequestContext(
    new HttpContextWrapper(HttpContext.Current),
    routeData);
    
var controllerFactory = ControllerBuilder.Current.GetControllerFactory();
 
var controller = controllerFactory.CreateController(
    requestContext,
    ErrorControllerRouteName);
    
controller.Execute(requestContext);
 
HttpContext.Current.Server.ClearError();

If you take this approach, you can log the exception and perform actions like clearing the response and setting its status code either in the module’s handler method or in the action method. If you chose the latter option, you can get the exception that was thrown using Server.GetLastError(). Regarding the status code, you have options as to how to set it. You could create an action method for each of the basic types of error and set the status code in that method. (For example, you could create an action method for handling server errors and have it set the response status code to 500.) Alternatively, you could determine the status code to use from within the action method by checking the exception that was thrown. I use the route data to pass the status code to the action method, but this is only really because of the particular needs of the demo application.

This approach feels like less of a hack than the view result approach, but it could have issues: I once saw a comment – probably on Stack Overflow – that this approach does not work if the original request fails request validation. You can try this scenario out for yourself using the demo application and the Request validation error link.

Experiment: Request validaton exception
Manual Error Handling – 500 error handling Application Error Handler
Manual Error Handling – App error handler response Controller Execute
All other settings Default values
Error link to click Request validation error

You should get the expected custom error page. This suggests that there is no issue with request validation failures.

The TransferRequest Approach

The following experiment shows you this approach in action.

Experiment: The TransferRequest Approach
Manual Error Handling – 500 error handling Application Error Handler
Manual Error Handling – App error handler response Transfer Request
All other settings Default values
Error link to click Action method exception on GET request

You should get the expected custom error page, with a 500 error status code and no redirect.

You saw in the previous post how the Server.Transfer method does not work for MVC URLs. There is, however, an alternative method called Server.TransferRequest that does. It was introduced in .NET 3.5 and it only works in IIS version 7.0 or higher, and only if the Web site’s application pool is running ASP.NET in Integrated Mode. Thomas Marquardt wrote a blog post that includes the following description of the method:

It is essentially a server-side redirect. When invoked, a new request context is created, called the child request, and it goes through the entire IIS 7.0/ASP.NET integrated pipeline. The child request can be mapped to any of the handlers configured in IIS, and it’s [sic] execution is not different than it would be if it was received via the HTTP stack. The parent request jumps ahead in the pipeline to the end request notification, and waits for the child request to complete. When the child request completes, the parent request executes the end request notifications and completes itself.

The key here is that the client never knows that a new request has been made; no redirect is issued to the client.

If you look at the Application_Error method in the ErrorHandlerHttpModule class, you will see that I create the URL to redirect to and append the error status code to it as a query string parameter. I also require that the method should discard any existing query string and form parameters, and use the “GET” HTTP method when making the request:

HttpContext.Current.Session["exception"] = exception;
 
var url = "~/Error/ManualErrors?statusCode=" + statusCode;
HttpContext.Current.Server.TransferRequest(url, false, "GET", null);

You cannot use Server.GetLastError in your custom error page to access the exception that was thrown because a new request is made for the error page, and Server.GetLastError only returns the last error for the current request. So to log the exception, you have to either log it in the module’s handler method or you have to pass the exception via session state to the action method code. I chose to take the latter approach. Also, I chose to pass the status code to the action method via a query string parameter, but you could determine the status code value in the action method from the passed exception.

Since Server.TransferRequest only works with IIS version 7.0 or greater in Integrated mode, it does not work with the Visual Studio Development Server. You can check this for yourself by pressing Ctrl+F5 in Visual Studio and repeating the last experiment. You should see a detailed YSOD. What actually happens is that an exception is thrown by Server.TransferRequest with the message, “This operation requires IIS integrated pipeline mode.” Because this exception gets thrown, the ASP.NET framework passes the original exception on to Custom Errors to be handled.

There is an additional issue: TempData does not work with Server.TransferRequest, so you cannot use TempData to transfer data from the parent request to the child request.

The MvcHttpHandler Approach

The following experiment shows you this approach in action.

Experiment: The MvcHttpHandler Approach
Manual Error Handling – 500 error handling Application Error Handler
Manual Error Handling – App error handler response Mvc Http Handler
All other settings Default values
Error link to click Action method exception on GET request

You should find that a detailed YSOD is returned, with a 500 error status code and with no redirect. What has actually happened is that an exception has been thrown by the MvcHttpHandler class, one with the message”‘HttpContext.SetSessionStateBehavior’ can only be invoked before ‘HttpApplication.AcquireRequestState’ event is raised.” Because this exception gets thrown, the ASP.NET framework passes the original exception on to Custom Errors to be handled. However, if you repeat this experiment using the Visual Studio Development Server, you will find that you do get the expected custom error page.

What this approach does is create an instance of the class that handles MVC page requests – MvcHttpHandler – and then pass it the URL of the error page to display. If you look at the Application_Error method in the ErrorHandlerHttpModule class, you will see how I have done this:

var path = HttpContext.Current.Request.Path;
 
HttpContext.Current.RewritePath("~/Error/ManualErrors");
HttpContext.Current.Session["statusCode"] = statusCode;
 
IHttpHandler handler = new MvcHttpHandler();
handler.ProcessRequest(HttpContext.Current);
 
HttpContext.Current.RewritePath(path);
HttpContext.Current.Server.ClearError();

I pass the status code to the error page using session state, but you could determine the status code value within the error page action method after you have invoked Server.GetLastError to get the exception.

Combining Server.TransferRequest and MvcHttpHandler

If you want to use Server.TransferRequest but you still want to support running the site via the Visual Studio Development Studio (and I think you would want to), you can combine the two approaches. This is done by using the HttpRuntime.UsingIntegratedPipeline property to detect which method to use:

if (HttpRuntime.UsingIntegratedPipeline)
{
    // TransferRequest approach.
}
else
{
    // MvcHttpHandler approach.
}

Errors Within Errors

It is possible that the custom error page you are showing could itself throw an exception. You can simulate this scenario for any of the custom error pages in the demo application if you set the Error page rendering mode to Throw Exception. The following are the important points to be aware of if this happens:

  • Generally the result will be whatever Custom Errors is configured to show. If the error page exception is thrown from within Application_Error, it will be the original exception and not the new exception that is passed to Custom Errors.
  • If Custom Errors is configured to show a custom error page and that page throws an exception, it will show a basic YSOD instead.
  • If you are using the HandleErrorAttribute filter and it throws an exception, it is that exception and not the original exception that is passed to Custom Errors.
  • If you are handling errors in Application_Error and are using Server.TransferRedirect to show the error page, IIS will try the transfer redirect each time the error page throws an exception, up to a total of ten times. If those ten attempts fail then IIS falls back to showing the built-in HTTP Errors error page, detailed or basic according to the current HTTP Errors configuration.

There are various options for how you can deal with exceptions thrown by error pages, and they depend on how you choses to handle errors in your application. For now, I only want to make you aware of the problem. In the last post of this series, I will be considering how the various error handling features I have discussed can be combined into strategies for handling errors in an ASP.NET MVC Web application. When I do that, I will also cover how each strategy handles exceptions thrown by error pages.

Finally

This post has covered two ways of manually handling errors in ASP.NET MVC Web applications: the HandleErrorAttribute filter and the HttpApplication.Error event. In the next post, we will move on to ways of handling 404 Not Found errors.

ASP.NET MVC gives you more options in the way that you handle exceptions. Error handling isn’t intrinsically exciting, but there are many ways of avoiding the classic yellow page of death, even getting ELMAH to manage error handling for you.

Years ago,  ASP.NET’s error handling was one of the major things that made me wonder if ASP.NET MVC could give me something that ASP.NET Web Forms couldn’t. Web Forms is based on pages; so if something goes wrong, all that you can do is to redirect the user to another page and explain what the error was or just be generically sorry. ASP.NET Web Forms allow you to map an error page for each possible HTTP status code. You control the mapping through the <customErrors> section of the web.config file.

Because of the  different architecture of the view in ASP.NET MVC,  it  is possible to save the redirect command and then programmatically switch to an error view in the context of the same request. You have this in addition to the regular page-based error handling mechanism. I wouldn’t use HTTP code redirects in ASP.NET MVC; but only because more flexible solutions are possible.

Generally speaking, error handling in ASP.NET MVC is mainly necessary to handle program and route exceptions. Program exceptions refer to catching errors in controllers and in any code you may have in Razor views. Route exceptions refer to missing links and invalid URLs.

Program Exceptions

Any stack trace you can have out of an ASP.NET MVC application originates from a method call in a controller class. The controller class, therefore, is where any exceptions in your ASP.NET MVC code can be trapped. You can do that in a number of equivalent ways. For example, you can have a try/catch block surrounding the entire method body. It works, but it’s ugly to see too. A better option is probably to override the OnException method from the base Controller class. Yet another option is using the HandleError attribute at the controller class level. Better yet, the HandleError attribute-which is ultimately an action filter-can be set globally on just every controllers and actions you can have.

At the end of the day, an effective strategy for error handling is based on the following pillars:

  • All classes down the controller level just throw exceptions when something goes wrong.
  • Some of these classes, in some of their methods, may attempt to catch some of the exceptions but mostly when a given exception is intended to be swallowed or turned into some other exception.
  • At the application level you use the HandleError global filter to catch whatever bubbles up.

Swallowing exceptions is in general a dangerous practice; but in the end it is not more dangerous than crossing the street when it’s red but there are no cars in sight. It works well as long as it doesn’t become a common practice and as long as it’s applied with a grain, or maybe two, of salt. Swallowing an exception is fine for example if your code is trying to call an external HTTP endpoint and the call times out or fails for whatever reason. In this case it might be acceptable that the routine that takes care of the call just hides the actual HTTP status code and packs the event as a special case of the regular response. Here’s an example taken from a data access repository class:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

public Order FindByCustomerAndId(int id, string customerId)

{

using (var db = new MyAppEntities())

 {

try

{

var order = (from o in db.Orders

where o.OrderId == id && o.Buyer.CustomerId == customerId

select o).Single();

return order;

}

catch (InvalidOperationException)

{

return new NullOrder();

}

}

}

In LINQ, the Single method just throws an exception if the response includes any number of items different from one. The internal try/catch block swallows the exception and returns a special version of the Order type that just evaluates to NULL.

The NullOrder class is an instance of the Special Case pattern and has the merit of not killing polymorphism in code as NULL would do. The caller of the aforementioned method will have then the following skeleton:

var order = _orderRepository.FindByCustomerAndId(orderId, customerId);

if (order is NullOrder)

{

...

}

There are a few guidelines you might want to adhere to when it comes to handling exceptions. The first aspect to consider is that the catch block is quite expensive and raises a peak of CPU usage when your code gets into it. For this reason, over-catching may end up affecting the overall performance of the application. It’s probably not a big deal if your code is frontend; but for server-side code scaling up the performance of the entire system it might become problematic.

A guideline from the .NET Framework team is that you never throw an exception using the System.Exception class. You should use more specific exception types whether built-in types such as InvalidOperationException and NullReferenceException or your own application specific types. On the other hand, you should also resist the temptation of having your own exception types sprinkled everywhere and even replacing analogous .NET Framework native types.

When it comes to exceptions, you should be very specific about the exception-type that you pick up and should also create instances providing as much information as possible. For example, ArgumentNullException is way more specific than ArgumentException. If the problem consists in an unexpected NULL parameter then you should go for ArgumentNullException. Furthermore, be aware that any exceptions come with a message. Provide details within the message as the message itself is targeted to developers. Another parameter of exception types that is often neglected is the name of the parameter where the exception occurred-mention it every time. It can be a lifesaver sometimes.

The OnException Method

In ASP.NET MVC, any method of any controller runs under the aegis of a system component known as the action invoker. The invoker runs all the code within a try/catch block and simply re-throws a thread-abort exception. For all other exceptions, instead, it goes through the list of registered action filters and gives each a chance to recover from the exception. At the end of the loop, if the exception has not been marked as handled, the exception originally caught is re-thrown. What happens next depends on whether you have other mechanism of exception handling set to watch over the entire application. If none is in place, which is the default, users will experience the ASP.NET classic yellow page of death or any other error page you arranged.

An action filter enabled to handle exceptions can be a separate class defined as an action filter (inherit from the ActionFilter class) or it can simply be a controller class that overrides the OnException method. This method is always invoked when an unhandled exception occurs in the course of the action method.

protected override void OnException(ExceptionContext filterContext)

{

...

}

It’s important to be aware that no exception that originates outside the controller will be caught by OnException. An excellent example of an exception not being caught by OnException is a ‘null reference’ exception that results in the model-binding layer. Another example is ‘route not-found’ exception.

The code in OnException has the power of controlling the entire response for the request that failed. As shown above, the method receives a parameter of type ExceptionContext which has an ActionResult property named Result. This property just refers to the next view or result past the method. If you set the Result property you can control the next screen; if you omit setting any result, then the user will see just a blank screen. Here’s a typical implementation of OnException:

protected override void OnException(ExceptionContext filterContext)

{

// Let other exceptions just go unhandled

if (filterContext.Exception is InvalidOperationException)

{

// Default view is «error«

filterContext.SwitchToErrorView();

}

}

The SwitchToErrorView method is an extension method for the ExceptionContext class with the following signature:

public static void SwitchToErrorView(this ExceptionContext context,

String view = «error«, String master = «»)

As you can see, you’re allowed to indicate the next view and even its layout.

The HandleError Attribute

If you don’t like the explicit override of OnException you can decorate the class (or just individual methods) with the HandleError attribute. 

[HandleError]

public class HomeController

{

...

}

As mentioned, HandleError is an action filter and not a plain attribute carrying just a bunch of metadata. In particular, HandleError implements the IExceptionFilter interface:

public interface IExceptionFilter

{

void OnException(ExceptionContext filterContext);

}

Internally, HandleError implements OnException using a piece of code very similar to the SwitchToErrorView method discussed earlier. A minor difference is that HandleError doesn’t trap any exceptions resulting from child actions. Properties on the attribute lets you select the exceptions to trap and views to redirect to.

[HandleError(ExceptionType=typeof(ArgumentException), View=«generic«)]

Each method can have multiple occurrences of the attribute, one for each exception you’re interested in. By default, also HandleError redirects to the same view named error we considered earlier. Note that such a view is purposely created by the ASP.NET MVC templates in Visual Studio.

When using HandleError at development time, it’s crucial to be aware that the attribute doesn’t have any effect unless you enable custom errors at the application level:

<customErrors mode=«On«>

</customErrors>

When you go live, remote users will correctly receive the selected error page regardless. To test the feature, though, you need to change the configuration file.

HandleError can be automatically applied to any method of any controller class by registering it as a global filter in global.asax:

public class MvcApplication : System.Web.HttpApplication

{

protected void Application_Start()

{

RegisterGlobalFilters(GlobalFilters.Filters);

...

}

public static void RegisterGlobalFilters(GlobalFilterCollection filters)

{

filters.Add(new HandleErrorAttribute());

}

}

Global filters are automatically added to the list of filters before the action invoker calls out any action method.

All Other Possible Errors

An error can always find its way to the user. For this reason, we’ve been given the Application_Error method in global.asax  ince the very first version of the ASP.NET runtime. It is just there to handle any possible errors that passed through try/catch blocks. The Error event fires whenever an unhandled exception reaches the outermost shell of ASP.NET code.  It’s the final call for developer’s code before the yellow screen of death.

You could do something useful in this event handler, such as sending an email or writing to the event log. 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

void Application_Error(Object sender, EventArgs e)

{

var exception = Server.GetLastError();

if (exception == null)

return;

var mail = new MailMessage { From = new MailAddress(«automated@contoso.com«) };

mail.To.Add(new MailAddress(«administrator@contoso.com«));

mail.Subject = «Site Error at « + DateTime.Now;

mail.Body = «Error Description: « + exception.Message;

var server = new SmtpClient { Host = «your.smtp.server« };

server.Send(mail);

// Clear the error

Server.ClearError();

// Redirect to a landing page

Response.Redirect(«home/landing«);

}

While you can always write the Error handler yourself, ASP.NET developers often use ELMAH. At the very end of the day, ELMAH is an HTTP module that, once configured, intercepts the Error event at the application level and logs it according to the configuration of a number of back-end repositories. The bottom line is that with ELMAH you can handle errors in many more ways and change /add actions with limited work; and without writing much code yourself.  ELMAH also offers some nice facilities, such as a web page you can use to view all recorded exceptions and drill down into each of them.  

ELMAH is an open-source project available at http://code.google.com/p/elmah. It is so popular that it counts a number of extensions, mostly in the area of repositories. To integrate it in your applications the easiest path you can take is the Nuget package you find at http://www.nuget.org/packages/elmah/1.2.2.

Summary

Error handling is one of the most bothersome parts of software development. One of the reasons that developers avoid it is that it doesn’t seem to require much creativity. In this regard, I think that ELMAH is emblematic. Nearly any developers knows that an HTTP module could do the trick of saving rewriting the same code over and over again to send emails and log errors on ASP.NET sites. And I guess as many developers had, in the past, a thought crossing their minds about writing a sort of simple but effective infrastructure for error handling and reporting. That’s just what ELMAH is-and that’s what ASP.NET developers need. Oh well, in addition to ad hoc try/catch blocks in the code.

Понравилась статья? Поделить с друзьями:
  • System thread exception not handled windows 10 как исправить ошибку
  • System thread exception not handled win 10 ошибка
  • System thread exception not handled win 10 как исправить
  • System temperature 90d hp как исправить
  • System stack error