Implement Global Exception Handling In ASP.NET Core Application

Introduction 

 
Today, in this article we will discuss the exception handling concept in any ASP.NET Core application. Exception handling is one of the most import functionality or part for any type of application which always need to be taken care and implement properly. Exceptions are mainly means for the run time errors which occur during the execution time of the application. So, if this type of error is not properly handled, then the application will be terminated.
 
In ASP.NET Core, the concept of exception handling has been changed, and rather to say, now it is in very much in better shape to implement exception handling. For any API projects implementing exception handling against every action, the method is quite time-consuming and it also requires extra efforts. So, for this purpose, we can implement the Global Exception handler so that all types of unhandled exceptions can be caught in this handler. The benefit of implementing a global exception handler is that we need to define this in one place. Through this handler, any exception that occurs in our application will be handled, even we ann new methods or controllers. So, in this article, we will discuss how to implement global exception handling in the ASP.NET Core Web API.
 

Create ASP.NET Core Web API Projects in Visual Studio 2019

 
So, before going to discuss the global exception handler, first, we need to create an ASP.NET Web API project. For this purpose, follow the steps mentioned below,
  • Now open the Microsoft Visual Studio and Click on Create a New Project
  • In the Create New Project dialog box, select ASP.NET Core Web Application for C# and then click on the Next Button.
Implement Global Exception Handling In ASP.NET Core Application
  • In the Configure your new project window, provide the project name and then click on the Create button.
  • In the Create a New ASP.NET Core Web Application dialog, select API, and then click on Create Button.
  • Ensure that the checkboxes “Enable Docker Support” and “Configure for HTTPS” are unchecked. We won’t be using these features.
  • Ensure that “No Authentication” is selected as we won’t be using authentication either.
  • Click OK

Use the UseExceptionHandler middleware in ASP.NET Core

 
So, to implement the global exception handler, we can use the benefits of the ASP.NET Core build-in Middleware. A middleware is indicated as a software component inserted into the request processing pipeline which handles the requests and responses. We can use the ASP.NET Core in-build middleware UseExceptionHandler to use as a global exception handler. 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.
 
Through this middleware, we can get all the detailed information of the exception object like the Stack trace, inner exception, message, etc., and also return that information through the API to return as an output. We need to put the exception handler middleware inside the configure() of a startup.cs file. If we use any MVC based application, then we can use the exception handler middleware just as below. This code snippet demonstrates how we can configure the UseExceptionHandler middleware to redirect the user to an error page when any type of exception has occurred.
  1. public void Configure(IApplicationBuilder app, IWebHostEnvironment env)  
  2. {  
  3.     app.UseExceptionHandler("/Home/Error");  
  4.     app.UseMvc();  
  5. }  
Now, we need to check this exception message. For that purpose, open the WeatherForecastController.cs file and add the below action method to throw an exception –
  1. [Route("GetExceptionInfo")]  
  2. [HttpGet]  
  3. public IEnumerable<string> GetExceptionInfo()  
  4. {  
  5.      string[] arrRetValues = null;  
  6.      if (arrRetValues.Length > 0)  
  7.      { }  
  8.      return arrRetValues;  
  9. }  
If we want to capture the details of the exception objects – i.e. like the stack trace, message, etc then we use the below code as the exception middleware –
  1. app.UseExceptionHandler(  
  2.                 options =>  
  3.                 {  
  4.                     options.Run(  
  5.                         async context =>  
  6.                         {  
  7.                             context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;  
  8.                             context.Response.ContentType = "text/html";  
  9.                             var exceptionObject = context.Features.Get<IExceptionHandlerFeature>();  
  10.                             if (null != exceptionObject)  
  11.                             {  
  12.                                 var errorMessage = $"<b>Exception Error: {exceptionObject.Error.Message} </b> {exceptionObject.Error.StackTrace}";  
  13.                                 await context.Response.WriteAsync(errorMessage).ConfigureAwait(false);  
  14.                             }  
  15.                         });  
  16.                 }  
  17.             );  
For checking the output, just execute the API endpoint in any browser:
 
Implement Global Exception Handling In ASP.NET Core Application
 

Define a Custom Exception Middleware to handle Exceptions in ASP.NET Core API

 
Also, we can write our custom middleware to handle any type of exceptions. In this section, we demonstrate how to create a typical custom middleware class. Custom middleware also provides much more flexibility to handle exceptions. We can add a stack trace, an exception type name, error code, or anything else which we want to include as a part of the error message. The below code snippet shows the typical custom middleware class:
  1. using Microsoft.AspNetCore.Http;    
  2. using Newtonsoft.Json;    
  3. using System;    
  4. using System.Collections.Generic;    
  5. using System.Linq;    
  6. using System.Net;    
  7. using System.Threading.Tasks;    
  8.     
  9. namespace API.DemoSample.Exceptions    
  10. {    
  11.     public class ExceptionHandlerMiddleware    
  12.     {    
  13.         private readonly RequestDelegate _next;    
  14.     
  15.         public ExceptionHandlerMiddleware(RequestDelegate next)    
  16.         {    
  17.             _next = next;    
  18.         }    
  19.     
  20.         public async Task Invoke(HttpContext context)    
  21.         {    
  22.             try    
  23.             {    
  24.                 await _next.Invoke(context);    
  25.             }    
  26.             catch (Exception ex)    
  27.             {    
  28.                     
  29.             }    
  30.         }    
  31.     }    
  32. }    
In the above class, a request delegate is passed to any middleware. The middleware either processes this or passes it to the next middleware in the chain. If the request is unsuccessful, then an exception will be thrown, and then the HandleExceptionMessageAsync method will be executed within the catch block. So, let's update the Invoke method code as shown below:
  1. public async Task Invoke(HttpContext context)  
  2.         {  
  3.             try  
  4.             {  
  5.                 await _next.Invoke(context);  
  6.             }  
  7.             catch (Exception ex)  
  8.             {  
  9.                 await HandleExceptionMessageAsync(context, ex).ConfigureAwait(false);  
  10.             }  
  11.         }  
Now, we need to implement the HandleExceptionMessageAsync method, as shown below:
  1. private static Task HandleExceptionMessageAsync(HttpContext context, Exception exception)  
  2.         {  
  3.             context.Response.ContentType = "application/json";  
  4.             int statusCode = (int)HttpStatusCode.InternalServerError;  
  5.             var result = JsonConvert.SerializeObject(new  
  6.             {  
  7.                 StatusCode = statusCode,  
  8.                 ErrorMessage = exception.Message  
  9.             });  
  10.             context.Response.ContentType = "application/json";  
  11.             context.Response.StatusCode = statusCode;  
  12.             return context.Response.WriteAsync(result);  
  13.         }  
Now, in the next step, we need to create a static class named ExceptionHandlerMiddlewareExtensions and add the below code within that class,
  1. using Microsoft.AspNetCore.Builder;  
  2. using System;  
  3. using System.Collections.Generic;  
  4. using System.Linq;  
  5. using System.Threading.Tasks;  
  6.   
  7. namespace API.DemoSample.Exceptions  
  8. {  
  9.     public static class ExceptionHandlerMiddlewareExtensions  
  10.     {  
  11.         public static void UseExceptionHandlerMiddleware(this IApplicationBuilder app)  
  12.         {  
  13.             app.UseMiddleware<ExceptionHandlerMiddleware>();  
  14.         }  
  15.     }  
  16. }  
Now, in the last step, we need to turn on our custom middleware within the Configure method of the startup class, as shown below:
  1. app.UseExceptionHandlerMiddleware();  

Conclusion

 
Exception handling is a mainly cross-cutting concept for any type of application. In this article, we discuss the implementation process of the global exception handling concept. We can take the benefits of global exception handling in any ASP.NET Core application to ensure that every exception will be caught and return the proper details related to that exception. With the global exception handling, we just need to write the exception handling related code for our entire application just in one place. Any suggestions or feedback or query related to this article are most welcome.