5 Exception Handling Schemes in ASP.NET Core

5 Exception Handling Schemes in ASP.NET Core

Exception handling is very important in programming, firstly to give users friendly prompts, and secondly for program security.

Last updated 4/29/2021 9:27 AM
.NET技术栈
4 min read
Category
ASP.NET Core
Tags
.NET C# ASP.NET Core Security Exception

Exception handling is very important in programming, both for providing user-friendly prompts and for program security. In ASP.NET Core, many solutions are already provided by default. The following is a summary.

1. Inherit from Controller and Override OnActionExecuted

When creating a new controller using VS, it inherits from a Controller class by default. Overriding the OnActionExecuted method and adding exception handling is sufficient. Generally, we create a BaseController and have all controllers inherit from it. The code is as follows:

public class BaseController : Controller
{
    public override void OnActionExecuted(ActionExecutedContext context)
    {
        var exception = context.Exception;
        if (exception != null)
        {
            context.ExceptionHandled = true;
            context.Result = new ContentResult
            {
                Content = $"BaseController错误 : { exception.Message }"
            };
        }
        base.OnActionExecuted(context);
    }
}

The advantage of this approach is its simplicity, but the obvious drawback is that if a cshtml page throws an error, it cannot be caught at all. However, if the project is purely a Web API project without views, this is acceptable.

2. Using ActionFilterAttribute

ActionFilterAttribute is an attribute that implements IActionFilter and IResultFilter, so in theory it can catch errors thrown both in actions and in views. We create a new ExceptionActionFilterAttribute, override OnActionExecuted and OnResultExecuted, and add exception handling. The complete code is as follows:

public class ExceptionActionFilterAttribute:ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext context)
    {
        var exception = context.Exception;
        if (exception != null)
        {
            context.ExceptionHandled = true;
            context.Result = new ContentResult
            {
                Content = $"错误 : { exception.Message }"
            };
        }
        base.OnActionExecuted(context);
    }

    public override void OnResultExecuted(ResultExecutedContext context)
    {
        var exception = context.Exception;
        if (exception != null)
        {
            context.ExceptionHandled = true;
            context.HttpContext.Response.WriteAsync($"错误 : {exception.Message}");
        }
        base.OnResultExecuted(context);
    }
}

There are two ways to use it:

  1. Add the [TypeFilter(typeof(ExceptionActionFilter)] attribute to the controller.
  2. Register it globally as a filter in the Startup.
services.AddControllersWithViews(options =>
{
    options.Filters.Add<ExceptionActionFilterAttribute>();
})

3. Using IExceptionFilter

As we know, ASP.NET Core provides five types of filters, and IExceptionFilter is one of them. As the name suggests, it is used to handle exceptions. In ASP.NET Core, ExceptionFilterAttribute already implements IExceptionFilter, so we only need to inherit from ExceptionFilterAttribute and override its methods. Similarly, create a new CustomExceptionFilterAttribute inheriting from ExceptionFilterAttribute, override OnException, and add exception handling. The complete code is as follows:

public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
    public override void OnException(ExceptionContext context)
    {
        context.ExceptionHandled = true;
        context.HttpContext.Response.WriteAsync($"CustomExceptionFilterAttribute错误:{context.Exception.Message}");
        base.OnException(context);
    }
}

Note that ExceptionFilterAttribute also provides an asynchronous method OnExceptionAsync. You only need to override one of these two methods. If both are overridden, both methods will be executed once.

There are two ways to use it:

  1. Add the [CustomExceptionFilter] attribute to the controller.
  2. Register it globally as a filter in the Startup.
services.AddControllersWithViews(options =>
{
    options.Filters.Add<CustomExceptionFilterAttribute>();
})

4. Using ExceptionHandler

In the startup file, the project created by VS will have the following code by default:

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Home/Error");
}

The meaning of the code above is: in the development environment, use app.UseDeveloperExceptionPage(). In the production environment, it redirects to the /Home/Error page.

UseDeveloperExceptionPage provides specific error information, including Stack trace, Query, Cookies, and Headers. After .NET Core 3.1, there are some changes: if the request header includes accept: text/html, it returns an HTML page; otherwise, it only returns the error.

Note: app.UseDeveloperExceptionPage() should be placed at the very beginning.

if (!env.IsDevelopment())
 {
     app.UseDeveloperExceptionPage();
 }
 else
 {
     app.UseExceptionHandler(builder =>
     {
         builder.Run(async context =>
         {
             var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>();
             await context.Response.WriteAsync($"error:{exceptionHandlerPathFeature.Error.Message}");
         });
     });
 }

5. Custom Middleware Handling

Handle globally through middleware.

public class ErrorHandlingMiddleware
{
   private readonly RequestDelegate next;

   public ErrorHandlingMiddleware(RequestDelegate next)
   {
        this.next = next;
   }

   public async Task Invoke(HttpContext context)
   {
        try
        {
           await next(context);
        }
        catch (System.Exception ex)
        {
           //处理异常
        }
   }
}
Keep Exploring

Related Reading

More Articles