How to implement global exception handling in ASP.Net Core MVC

Take advantage of global exception handling to avoid writing scads of boilerplate exception handling code in ASP.Net Core MVC

How to implement global exception handling in ASP.Net Core MVC
Alengo

Exceptions are runtime errors that might occur in your application. If exceptions are not handled properly, the program in execution is terminated. This article presents a discussion of how global error handling can be implemented in ASP.Net Core MVC, along with relevant code examples and screen images to illustrate the concepts. 

To work with the code examples in this article, you should have Visual Studio 2019 installed in your system. If you don’t already have a copy, you can download Visual Studio 2019 here

Create an ASP.Net Core MVC project in Visual Studio

First off, let’s create an ASP.Net Core project in Visual Studio 2019. Assuming Visual Studio 2019 is installed in your system, follow the steps outlined below to create a new ASP.Net Core project in Visual Studio.

  1. Launch the Visual Studio IDE.
  2. Click on “Create new project.”
  3. In the “Create new project” window, select “ASP.Net Core Web Application” from the list of templates displayed.
  4. Click Next.
  5. In the “Configure your new project” window, specify the name and location for the new project.
  6. Optionally, select the “Place solution and project in the same directory” check box.
  7. Click Create.
  8. In the “Create a New ASP.Net Core Web Application” window shown next, select .Net Core as the runtime and ASP.Net Core 2.2 (or later) from the drop-down list at the top.
  9. Select “Web Application (Model-View-Controller)” as the project template to create a new ASP.Net Core MVC application. 
  10. Ensure that the check boxes “Enable Docker Support” and “Configure for HTTPS” are unchecked as we won’t be using those features here.
  11. Ensure that Authentication is set to “No Authentication” as we won’t be using authentication either.
  12. Click Create. 

Following these steps should create a new ASP.Net Core MVC project in Visual Studio. We’ll use this project in the sections below to illustrate handling null responses in ASP.Net Core MVC.

Global exception handling options in ASP.Net Core MVC

Support for global exception handling is built into ASP.Net Core MVC. You can take advantage of global exception handling middleware to configure exception handling at a central place and hence reduce the amount of exception handling code that you would otherwise have to write in your application.

A second approach to handling exceptions globally in ASP.Net Core MVC is to use exception filters. You can read about exception filters in my previous article here. In this article, we’ll use the built-in global exception handling middleware and the UseExceptionHandler method discussed in the next section.

Use the UseExceptionHandler extension method in ASP.Net Core

UseExceptionHandler is an extension method that registers the ExceptionHandler middleware with the request processing pipeline.

You can use the IExceptionHandlerFeature interface to retrieve the exception object. The following code snippet illustrates how the UseExceptionHandler method can be used to handle exceptions globally.

app.UseExceptionHandler(
             builder =>
             {
                 builder.Run(
                 async context =>
                 {
                     context.Response.StatusCode =
                  (int)HttpStatusCode.InternalServerError;
                     context.Response.ContentType =
                     "application/json";
                     var exception =
                     context.Features.Get
                     <IExceptionHandlerFeature>();
                     if (exception != null)
                     {
                         var error = new ErrorMessage()
                         {
                             Stacktrace =
                             exception.Error.StackTrace,
                             Message = exception.Error.Message
                         };
                         var errObj =
                         JsonConvert.SerializeObject(error);
                         await context.Response.WriteAsync
                         (errObj).ConfigureAwait(false);
                     }
                 });
             }
        );

Here is the ErrorMessage class for your reference.

    public class ErrorMessage
    {
        public string Message { get; set; }
        public string Stacktrace { get; set; }
    }

Configure the global exception handling middleware in ASP.Net Core MVC

Once you have created a new ASP.Net Core MVC application, the Startup.cs file will contain a Configure method that configures global exception handling middleware as shown in the code snippet below.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
            }
            app.UseStaticFiles();
            app.UseCookiePolicy();
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template:
                    "{controller=Home}/{action=Index}/{id?}");
            });
        }

Note the statement app.UseExceptionHandler("/Error"); in the above code snippet. The UseExceptionHandler method registers the ExceptionHandler middleware and then routes the user to "/Error" whenever there is an unhandled error in the application.

You can take advantage of the UseStatusCodePagesWithReExecute extension method to add status code pages to the pipeline. You can use this method to handle the HTTP 404 errors as well. The following code snippet shows the updated Configure method.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
   {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                app.UseStatusCodePagesWithReExecute("/Error/NotFound/{0}");
            }
      //Other code
   }

Use the ErrorController class in ASP.Net Core MVC

Note that in the HomeController class you’ll find an action method to handle errors. We will not use this action method, so you can go ahead and delete it. Here is the ErrorController class that contains the action method for the "/Error" route.

public class ErrorController : Controller
    {
        [HttpGet("/Error")]
        public IActionResult Index()
        {
            IExceptionHandlerPathFeature
            iExceptionHandlerFeature =
            HttpContext.Features.Get
            <IExceptionHandlerPathFeature>();
            if (iExceptionHandlerFeature != null)
            {
                string path = iExceptionHandlerFeature.Path;
                Exception exception =
                iExceptionHandlerFeature.Error;
                //Write code here to log the exception details
                return View("Error",
                iExceptionHandlerFeature);
            }                
            return View();
        }
        [HttpGet("/Error/NotFound/{statusCode}")]
        public IActionResult NotFound(int statusCode)
        {
            var iStatusCodeReExecuteFeature =   
            HttpContext.Features.Get
            <IStatusCodeReExecuteFeature>();
            return View("NotFound",
            iStatusCodeReExecuteFeature.OriginalPath);
        }
    }

You can take advantage of IExceptionHandlerPathFeature to retrieve exception metadata. You can also take advantage of IStatusCodeReExecuteFeature to retreive the path where a HTTP 404 exception occurred. To leverage IExceptionHandlerPathFeature and IStatusCodeReExecuteFeature, ensure that the Microsoft.AspNetCore.Diagnostics NuGet package is installed in your project. The following statement illustrates how you can retrieve the route in which the exception has occurred.

string route = iExceptionHandlerFeature.Path;

To retrieve the exception details, you can write the following code.

var exception = HttpContext.Features.Get<IExceptionHandlerPathFeature>();

Once you have retrieved the route and exception details, you can write your own code to log the details as well.

Create the views to display error messages in ASP.Net Core MVC

You’ll need a View to display the error messages. Here is the code for the Error view page.

@model Microsoft.AspNetCore.Diagnostics.IExceptionHandlerFeature
@{
    ViewData["Title"] = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="row">
    <div class="text-danger">
        <h3>Error: @Model.Error.Message</h3>
    </div>
</div>
<div class="row">
    <div class="col-12">
        <p>@Model.Error.StackTrace</p>
        <p>@Model.Error.InnerException</p>
    </div>
</div>

Here is the code for the NotFound view page.

@model string
@{
    ViewData["Title"] = "NotFound";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
    <h1 class="text-danger">
    Error: The requested URL @Model was not found!</h1>
<hr />

Now, when you execute the application, you should see the following output for a global exception and an HTTP 404 error, respectively.

asp net mvc global exception IDG

Figure 1: An exception handled globally in ASP.Net Core MVC. 

And, here’s how HTTP 404 error would be handled if you try to browse an URL that is not existent.

asp net mvc http 404 error IDG

Figure 2: An HTTP 404 error handled globally in ASP.Net Core MVC.

The support for global exception handling is built into ASP.Net Core. You can leverage global exception handling in ASP.Net Core to handle all exceptions in a central place in your application, and to handle exceptions differently based on the environment —development, production, etc. — the application is running in.

Copyright © 2019 IDG Communications, Inc.