How to implement global exception handling in ASP.Net Core Web API

Take advantage of the UseExceptionHandler middleware in ASP.Net Core to ensure that every error in your Web API application is caught

Exceptions are runtime errors that occur in an application. If they are not handled properly, the running program is terminated. How can we prevent that from happening? We can ensure that all unhandled exceptions are caught by using a global exception handler.

To implement global exception handling in ASP.Net Core Web API, we can take advantage of the built-in middleware UseExceptionHandler. A middleware is a software component inserted into the request processing pipeline that handles requests and responses.

When building an ASP.Net Core application, we can leverage various middleware components to customize the handling of requests and responses, and even inspect, route, or modify the request and response messages that flow through the pipeline.

In this post I will show how we can use the UseExceptionHandler middleware to handle exceptions globally in ASP.Net Core Web API.

Create an ASP.Net Core Web API project in Visual Studio 2017

First off, let’s create an ASP.Net Core Web API project. If Visual Studio 2017 is up and running in your system, follow the steps outlined below to create an ASP.Net Core Web API project.

  1. Launch the Visual Studio 2017 IDE.
  2. Click on File > New > Project.
  3. Select “ASP.Net Core Web Application (.Net Core)” from the list of templates displayed.
  4. Specify a name for the project.
  5. Click OK to save the project.
  6. Select “.Net Core” as the runtime and ASP.Net Core 2.1 (or later) from the drop-down list at the top of the window.
  7. Select “API” as the project template.
  8. Ensure that the check boxes “Enable Docker Support” and “Configure for HTTPS” are unchecked. We won’t be using these features. 
  9. Ensure that “No Authentication” is selected as we won’t be using authentication either. 
  10. Click OK.

This will create a new ASP.Net Core Web API project. We will use this project to implement global exception handling in the sections that follow.

aspnet core global exception handling 1 IDG

Creating an ASP.Net Core Web API application in Visual Studio 2017. 

Use the UseExceptionHandler middleware in ASP.Net Core

The ASP.Net Core request processing pipeline includes a chain of middleware components. This pipeline in turn contains a series of request delegates that are invoked one after another. While the incoming requests flow through each of the middleware components in the pipeline, each of these components can either process the request or pass the request to the next component in the pipeline.

The following code snippet illustrates how you can configure the UseExceptionHandler middleware to redirect the user to an error page when an exception occurs.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseExceptionHandler("/Home/Error");
            app.UseMvc();
        }

If you would like to capture the details of the exception object — i.e., the stack trace, etc. — you can use the following code.

app.UseExceptionHandler(
                options =>
                {
                    options.Run(
                        async context =>
                        {
                            context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                            context.Response.ContentType = "text/html";
                            var exceptionObject = context.Features.Get<IExceptionHandlerFeature>();
                            if (null != exceptionObject)
                            {                              
                                var errorMessage = $"<b>Error: {exceptionObject.Error.Message}</b>{exceptionObject.Error.StackTrace}";
                                await context.Response.WriteAsync(errorMessage).ConfigureAwait(false);
                            }
                        });
                }
            );

Next, update the default Get action method in the ValuesController with the following code.

  [HttpGet]
  public ActionResult<IEnumerable<string>> Get()
   {
       throw new Exception("An error occurred...");
   }

Run the application and you’ll see the stack trace displayed in the web browser as shown in the figure below.

aspnet core global exception handling 2 IDG

The exception trace displayed in the web browser. 

Create a custom middleware to handle exceptions in ASP.Net Core Web API

You can also write your own middleware to handle exceptions. Here is an example that illustrates a typical custom middleware class. Writing your own custom middleware to handle exceptions allows for much more flexibility. You can add a stack trace, an exception type name, error code, or anything else you would like to include as part of the error message.

Here is an example that illustrates a typical custom middleware class.

public class CustomExceptionMiddleware
{
    private readonly RequestDelegate _next;
    public CustomExceptionMiddleware(RequestDelegate next)
    {
        _next = next;
    }
    public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next.Invoke(context);
        }
        catch (Exception ex)
        {
            //Write code to handle exception here
        }
    }
}

Note that a request delegate is passed to any middleware. The middleware then either processes it or passes it to the next middleware in the chain. If a request is unsuccessful, an exception will be thrown and the HandleExceptionAsync method will be called in the catch block.

Now let’s update the Invoke method with the following code.

public async Task Invoke(HttpContext context)
        {
            try
            {
                await _next.Invoke(context);
            }
            catch (Exception ex)
            {
                await HandleExceptionAsync(context,
                 ex).ConfigureAwait(false);
            }
        }

Here is the code of the HandleExceptionAsync method.

private static Task HandleExceptionAsync(HttpContext context, Exception exception)
        {
            context.Response.ContentType = "application/json";
            int statusCode = (int)HttpStatusCode.InternalServerError;
            var result = JsonConvert.SerializeObject(new {StatusCode = statusCode, ErrorMessage = exception.Message });
            context.Response.ContentType = "application/json";
            context.Response.StatusCode = statusCode;
            return context.Response.WriteAsync(result);
        }

And here is the complete code listing of the CustomExceptionMiddleware class for your reference.

public class CustomExceptionMiddleware
    {
        private readonly RequestDelegate _next;
        public CustomExceptionMiddleware(RequestDelegate next)
        {
            _next = next;
        }
        public async Task Invoke(HttpContext context)
        {
            try
            {
                await _next.Invoke(context);
            }
            catch (Exception ex)
            {
                await HandleExceptionAsync(context, ex).ConfigureAwait(false);
            }
        }
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
        {
            context.Response.ContentType = "application/json";
            int statusCode = (int)HttpStatusCode.InternalServerError;
            var result = JsonConvert.SerializeObject(new {StatusCode = statusCode,
               ErrorMessage = exception.Message });
            context.Response.ContentType = "application/json";
            context.Response.StatusCode = statusCode;
            return context.Response.WriteAsync(result);
        }
    }

Just two more steps. Next create a static class named ExceptionMiddlewareExtensions and include the following code.

public static class ExceptionMiddlewareExtensions
    {
        public static void UseCustomExceptionMiddleware(this IApplicationBuilder app)
        {
            app.UseMiddleware<CustomExceptionMiddleware>();
        }
    }

Finally, you can now turn on the custom middleware in the Configure method of the Startup class as shown in the code snippet below.

public void Configure(IApplicationBuilder app,
IHostingEnvironment env)
        {
            app.UseCustomExceptionMiddleware();
            app.UseMvc();
        }

When you run the application, exception output will be displayed in the web browser as shown in the figure below.

aspnet core global exception handling 3 IDG

Custom exception handling middleware in action. 

Exception handling is a cross-cutting concern in every application. Fortunately, we can take advantage of global exception handling in ASP.Net Core to ensure that every exception is caught, and to eliminate the need to write additional boilerplate code each time we write a new controller or controller method. With global exception handling, we only need to write the exception handling code for our application one time in one place. It is a good practice to write your own custom middleware for handling exceptions globally.

Copyright © 2018 IDG Communications, Inc.