Implementing exception handling middleware in .Net Core

08 december 2021 om 10:00 by ParTech Media - Post a comment

The world is moving towards a microservice architecture due to ease of management and maintenance. Web APIs play a key role in developing microservices. Web APIs are created to accept requests and send responses.

There are different types of REST protocols available, such as Get by Id, Get All, Update, Create and Delete. Each of them has a different response data that will be sent to the client.

For instance, Get by id sends a single object of data. On the other hand, Get All sends a list of objects as the response. Create, Update and Delete responses are dealt with according to the user's requirement. Sounds easy right?

This is true only till the code works in the happy path. But, when the code breaks and fails to process the request, it throws exceptions as a response to the clients. If clients do not handle the exceptions, then there are chances that the front end might crash.

Now there are chances that the issue might be at any line of the backend code. So, it is difficult to implement try and catch exceptions at every controller level as a similar code has to be implemented in all the places. This makes it cumbersome to maintain; it also increases redundancy.

To solve this problem, Microsoft has provided an exception handling middleware as a part of .Net Core which can be used to interrupt all the exceptions generated in the code and generate the response in a formatted way. Let’s understand more about this in this post.

Table of contents

  1. Introduction to ProblemDetails
  2. Implementation of ProblemDetails in a .Net core application
  3. Conclusion

Introduction to ProblemDetails

ProblemDetails middleware was created by Kristian Hellang and it was introduced from ASP .Net Core 2.2 version. It was intended to handle the exceptions that occur in the code and send a standardized response. ProblemDetails also provides options to pass on the details of the exception, status code, and any additional information along with it.

Implementation of ProblemDetails in .net core application

To implement ProblemDetails, we are going to create a Web API with .Net 5 and create a few exceptions in the code. We will then observe how it gets thrown from the code using Postman and later implement ProblemDetails on top of it. Again we will observe the change it makes to the error response.

First, let us create a .Net core Web API application and choose the framework as .Net 5. Provide a valid name for the solution.

A typical web API project will have the below files and folders -

Let's now open the WeatherForecast controller under the controllers' folder and edit the Get method inside it to throw exceptions. Now let's observe in Postman.

To do that, add the below line at the top of Get method (of the WeatherForecast controller) -

throw new InvalidOperationException("Explicit error.");

Now run the solution and try the below URL in Postman (Look for the configured port of the application and change it accordingly).

https://localhost:44344/weatherforecast

Did you observe that the stack trace of the error has been thrown as the response? This is not easily understandable for everyone.

Now, let's try to implement ProblemDetails and observe the response. To do that, install the below NuGet package.

Hellang.Middleware.ProblemDetails

Once done, we need to make a few changes to the Startup.cs file to get the response in the ProblemDetails class format.

In the configureservices method, add the below line

services.AddProblemDetails(); 

Also, add the below reference in the header

using Hellang.Middleware.ProblemDetails;

In the Configure method, above app.UseRouting(), add the below line.

app.UseProblemDetails();

Now, if we run the solution and notice the exception response, it is represented in a structured way and makes it more readable.

It doesn’t end here, it is possible to configure different types of exception responses for different exceptions.

For example, right now we have explicitly thrown InvalidOperationException for which we can configure the status code and message from the ProblemDetails side.

To do so, open the startup file. In the configureservices method, remove

services.AddProblemDetails(); 

And add the below lines

services.AddProblemDetails(opt =>

​      {

​        opt.IncludeExceptionDetails = (con,action) => false;

​        opt.Map<InvalidOperationException>(exp => new ProblemDetails

​        {

​          Title = "Invalid operation exception has been configured.",

​          Status = StatusCodes.Status500InternalServerError,

​          Detail = exp.Message

​        });

​      });

This indicates that the exception stack trace will not be displayed. It also specifies that wherever the InvalidOperationException occurs in the code, only the details (title, Status, and detail) would be sent in the response. Here, the title is a configured text, status is being set as 500 and Detail is the message that has been thrown from the place of exception.

And if we run the solution again and hit the get method, below JSON response would be displayed -

{

  "title": "Invalid operation exception has been configured.",

  "status": 500,

  "detail": "Explicit error.",

  "traceId": "00-43bf675d2262ba44849351d2b566d600-80214cb0b9856f4e-00"

}

This indicates that the ProblemDetails mapping is working.

Here, only the InvalidOperationException is being configured with the required response status and title message. In case of any other exception in the code, the exception response would be sent in the default ProblemDetails format.

If additional properties are required in the response along with the properties of ProblemDetails, then the ProblemDetails class can be extended and the new class can be mapped to the exception in the startup.

For example, to include date time in the exception response, the below class is created -

public class AdditonalDetails : ProblemDetails

  {
​    public DateTime DateTime { get; set; }
  }

Now, instead of mapping ProblemDetails in the startup, AdditionalDetails class will be mapped.

opt.Map<InvalidOperationException>(exp => new AdditionalDetails

​        {

​          Title = "Invalid operation exception has been configured.",

​          Status = StatusCodes.Status500InternalServerError,

​          Detail = exp.Message,

​          DateTime = DateTime.UtcNow

​        });

The below response will be obtained.

Do you notice the presence of datetime here? Similarly, any number of additional properties can be added.

Final Words

Thus without implementing any major code as a generic solution, exception handling has been done in a uniform format using the middleware option which is readily available in the .Net core side.