ASP. NET MVC Exception Handling Custom Error Page Detailed Analysis

  • 2021-10-24 19:25:52
  • OfStack

1. Application scenarios

For B/S applications, In the process of deploying to the formal environment, It is very likely that there will be 1 exception or error that was not found in the previous test. Or some exceptions that only occur when certain conditions are met, For application sites developed using ASP. NET MVC, After deployment to IIS, If the developer does not error-handle the program, Then 1 once the program has an unhandled error or exception, The user will see an error stack trace page that is extremely confusing. Make the user experience of the site decline, From a procedural point of view, Without custom error handling, it is not conducive to finding the root cause when the program goes wrong. Because many times some errors only reappear when certain conditions are met, If you miss it, it may take a lot of time to test and reproduce the problem. If the developer logs the runtime exceptions in the program at this time, it may provide some valuable error root information. Next, I will explain how to implement custom exception handling and jump to the friendly error prompt page.

2. Exception handling & Customize error page

1. Exception handling and customizing error pages through exception filters

asp. net mvc provides an exception filter to catch when an unhandled exception is thrown when executing an action method in controller. The exception filter in mvc exists in the form of characteristics (Attribute). It only takes two steps to define a custom exception filter:

1. Define a class, inherit FilterAttribute class, implement IExceptionFilter interface 2, and apply custom exception filter to specified action method or controller class or global application.

Exception filter code


using log4net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Blog20180413.Filters
{
 public class CustomExceptionFilterAttribute : FilterAttribute, IExceptionFilter
 {
 //log4net Component , Used for logging. 
 static readonly ILog log = LogManager.GetLogger(typeof(CustomExceptionFilterAttribute));
 public void OnException(ExceptionContext filterContext)
 {
  // Log the captured exception information , Convenient for developers to troubleshoot problems. 
  log.Error(" Application exception ", filterContext.Exception);

  // Jump to a custom error page , Enhance the user experience. 
  ActionResult result = new ViewResult() { ViewName = "CustomErrorPage" };
  filterContext.Result = result;
  // After exception handling, ,1 Be sure to put ExceptionHandled Set to true, Otherwise, errors will continue to be thrown. 
  filterContext.ExceptionHandled = true;
 }
 }
}

Use exception filters


using Blog20180413.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Blog20180413.Controllers
{
 public class TestExceptionHandleController : Controller
 {
 [CustomExceptionFilter]
 public ActionResult Index()
 {
  string str = string.Empty;
  // A conversion exception will be thrown 
  int result = int.Parse(str);
  return View();
 }
 }
}

Note:

As mentioned in step 2, You can apply custom exception filters only to action or controller, If you want to apply the specified exception filter as an attribute to only one or more of the specified controller or action and not to all controller or action, you must inherit the exception filter from the FilterAttribute class because the mvc framework obtains the exception filter attribute marked on the specified controller or action through FilterAttributeFilterProvider. GetFilters, and the internal logic requirements of GetFilters must inherit from the FilterAttribute class.

If you need to apply a custom exception filter to all controller's action, you need to register the custom exception filter globally, with the following code:


using Blog20180413.Filters;
using System.Web;
using System.Web.Mvc;

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

2. Exception handling and custom error pages are implemented by defining the Application_Error method in Global. asax

The custom exception filters mentioned above can only catch exceptions thrown during the execution of the action method (even if registered as global filters, they can only catch exceptions thrown during the execution of the action method), If you need to catch a higher-level exception, that is, any exception that occurs during the execution of the request (such as throwing an exception in the constructor of the controller), you can use this method with the following code:


using log4net;
using log4net.Config;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace Blog20180413
{
 public class MvcApplication : System.Web.HttpApplication
 {
  static readonly ILog log = LogManager.GetLogger(typeof(MvcApplication));
  protected void Application_Start()
  {
   AreaRegistration.RegisterAllAreas();
   FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
   RouteConfig.RegisterRoutes(RouteTable.Routes);
   XmlConfigurator.ConfigureAndWatch(new FileInfo(Server.MapPath("~/web.config")));
  }

  protected void Application_Error(object sender, EventArgs e)
  {
   Exception exception = Server.GetLastError();
   //Server.ClearError();
   // Error log information is recorded here 
   log.Error("MvcApplication  Catch exceptions ", exception);
   // Jump to the specified custom error page 
   Response.Redirect("/CustomErrorHandle/CustomErrorPage");
  }
 }
}

3. By configuring system. web- > The customErrors node implements a custom error page

When an exception occurs on your site, if you simply want to jump to a custom error page instead of taking the exception one step further, you can simply do the following configuration operations:

The following configuration is required in web. config:


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

Note: CustomErrorPage here is a view file, placed in the Shared shared directory.

If you register the HandleErrorAttribute exception filter to the global, you will get some information about the exception in your error page. But the value configured to defaultRedirect at this time must be Error

That is to say, the name of the custom error view page must be Error. cshtml and placed in the Shared directory. Of course, you can also set the View property during the process of creating the HandleErrorAttribute global filter, so that you don't have to say that the error view name is set to Error. Here are:


 public static void RegisterGlobalFilters(GlobalFilterCollection filters)
 {
    HandleErrorAttribute errorAttribute = new HandleErrorAttribute();
    errorAttribute.View = "CustomErrorPage";
    filters.Add(errorAttribute);
 }

Register HandleErrorAttribute (using the default error view page file name)


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

Define the Error. cshtml view page


@{
 Layout = null;
}
@model HandleErrorInfo
<!DOCTYPE html>
<html>
<head>
 <meta name="viewport" content="width=device-width" />
 <title>Error</title>
</head>
<body>
 <div>
  @* Pass HandleErrorAttribute The exception information captured by the exception filter is stored in the Model Attribute *@
  @Model.Exception.Message
 </div>
</body>
</html>

The reason why the exception information can be obtained in the error page by registering the exception captured by HandleErrorAttribute filter can be seen from the internal implementation of HandleErrorAttribute class. It is found that an HandleErrorInfo object was passed in the process of loading the error view page.


public virtual void OnException(ExceptionContext filterContext)
{
 if (filterContext == null)
 {
  throw new ArgumentNullException("filterContext");
 }
 if (!filterContext.IsChildAction && (!filterContext.ExceptionHandled && filterContext.HttpContext.IsCustomErrorEnabled))
 {
  Exception innerException = filterContext.Exception;
  if ((new HttpException(null, innerException).GetHttpCode() == 500) && this.ExceptionType.IsInstanceOfType(innerException))
  {
   string controllerName = (string) filterContext.RouteData.Values["controller"];
   string actionName = (string) filterContext.RouteData.Values["action"];
   HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
   ViewResult result = new ViewResult {
    ViewName = this.View,
    MasterName = this.Master,
    ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
    TempData = filterContext.Controller.TempData
   };
   filterContext.Result = result;
   filterContext.ExceptionHandled = true;
   filterContext.HttpContext.Response.Clear();
   filterContext.HttpContext.Response.StatusCode = 500;
   filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
  }
 }
}

public string View
{
 get
 {
  if (string.IsNullOrEmpty(this._view))
  {
   return "Error";
  }
  return this._view;
 }
 set => 
  (this._view = value);
}

3. Summary

In general, The Application_Error method handles exceptional errors that occur at the request pipeline level, The Mvc exception filter can only handle exceptions that occur during the execution of the action method. The range that can be handled is smaller than that of Application_Error, However, in the actual project development, using Mvc exception filter to handle exceptions will be 1 point more, because our functional business is often reflected in the process of action method execution of the controller, that is, exceptions are easy to occur in this process. Therefore, Mvc exception filter can meet most exception handling requirements in development.


Related articles: