This post will describe several ways to handle known Exceptions in a project.

For each possible implementation, we will do an overview of the flow that the exception does to understand the context of each implementation.

First, let’s do an overview of what is, normally, a basic flow in any Project.

The basic flow of an API Project

The previous Figure shows the basic flow of events inside the IDM project. The API receives an HTTP Request in a certain endpoint. The endpoints of the API are mapped in the controller layer, so when the API receives the HTTP request the controller is the first to interact with that request and sends that request to the respective service that will orchestrate all the necessities of that request. Those necessities could be, for example, retrieve some information from the Database, and that is done in the repository layer. The repository layer is where we map all the interactions with external services(DataBases, API, etc). This is a basic overview of what could be a basic API project.

The Exception Handling Problem

Taking into account the previous overview of the project, we want to make the process of handling exceptions the more clearest and efficient possible in terms of computational needs, but also in terms of code structure. To achieve this purpose we will describe three different ways of handling the exceptions.

Also, in this project, we have 2 different types of exceptions. They are:

  • DomainException – This is the type of exception that is thrown when the error that happens is something that the API already expects;
  • Exception – This could actually be any type of exception different from DomainException. This occurs when an unexpected error occurs in the API;
Reference to Middleware

In this case, we will use the ExceptionHandler middleware layer. The middleware layer is specific to .NET Core and we will not focus on that here. Take into account that we will use ExceptionHandler in the middleware pipeline to catch all the exceptions that are not treated.

Here is the reference to ASP.NET Core Middleware.

First Approach: Try-Catch on Controller Layer

This approach consists of catching and treating the DomainExceptions that are thrown by any of the layers (Controller, Service, or Repository) of the APIProject in the Controller layers. Take into account that all the unexpected exceptions are always treated in the ExceptionHandler of the Middleware.

Second Approach: Try-Catch in all Layers

This approach consists of all layers of the API Project being capable of catching exceptions and sending again the exception to the upper layers. The Controller layer is where the Domain Exceptions will be treated.

Take into account that in this case, the unexpected exceptions are caught by the layer that throws her and also by the rest of the upper layers. However, it is treated in the ExceptionHandler of the Middleware.

Third Approach: Encapsulate Exception in Response Object

This approach consists, as the name says, in encapsulating the exception in the Response Object. Instead of throwing the exception, we will send the Response object with a Failed message with an instance of the exception in the Response Object as a variable. This means that the system will not need to operate the throws of exceptions for the expected errors. They will be passed in the response object.

Take into account that only the unexpected exceptions are thrown to Middleware – ExceptionHandler to be treated.

Normal Response Object

public class OperationResult
{
        public OperationResult();

        public static OperationResult Success { get; }
        public bool Succeeded { get; protected set; }
        public IEnumerable<OperationError> Errors { get; }

        public static OperationResult Failed(params OperationError[] errors);
        public override string ToString();
}

New Response Object for this approach

public class OperationResult
{
        public OperationResult();
           
        public static OperationResult Success { get; }
        public bool Succeeded { get; protected set; }
        public IEnumerable<OperationError> Errors { get; }

        public static OperationResult Failed(params OperationError[] errors);
        public override string ToString();
        public Exception ExceptionHolder { get; set;} 
}

Fourth Approach: Catch all exceptions in Middleware – Exception Handler

The approach consists of throwing the exceptions whenever they happen and then let them be caught and treated by the middleware – ExceptionHandler. In this approach, we would create a different type of response for each type of exception for the ExceptionHandler send as Response.

This would centralize all the exception handling in one place.

Comparison of Exception Handling approaches

Exception Handling ApproachProsCons
First Approach: Try-Catch on Controller LayerOne place to treats the expected exceptions and another for the unexpected;

No need to make calls to the error page when is an expected error, thus making the operation quicker;

Only adds exception related code in the controller Layer;
Catch & Throw of exception in the controller layer uses more computational resources;

Every endpoint mapped in the controller will need try-catch;
Second Approach: Try-Catch in all LayersEasy to know where the exception happened and where it is (easy to understand the flow);

One place to treats the expected exceptions and another for the unexpected;
Exception handling code everywhere;

Can cause a bottleneck in the app because of the inherent addition of time to the response and use of computational resources;

Repeated Code;

Make logical code harder to understand;
Third Approach: Encapsulate Exception in Response ObjectThe runtime doesn’t need to handle the throw of expected Exceptions;

One place to treats the expected exceptions (in this case inside the response Object) and another for the unexpected;

App maintains a normal flow even to it occurred an expected exception;
Handling of the expected exceptions is not explicit;
Fourth Approach: Catch all exceptions in Middleware – Exception HandlerAggregates the handling of all exceptions in one place;

Exception handling code is in one place and logical/business code in other;
Unexpected and Expected exceptions handling are in the same place;

Add another request to the operation because it will always need to request the errors page when an exception happens (Middleware- Exception Handler Implementation);

LEAVE A REPLY

Please enter your comment!
Please enter your name here