Question3r
Question3r

Reputation: 3832

Map custom exceptions to HTTP exceptions at one place

In my .NET Core Web API project many controller endpoints have code like this example

    public async Task<ActionResult<User>> UpdateUserUsernameAsync(/* DTOs */)
    {
        try
        {
            User user = null; // Update user here
            
            return Ok(user);
        }
        catch (EntityNotFoundException entityNotFoundException) // map not found to 404
        {
            return NotFound(entityNotFoundException.Message);
        }
        catch (EntityAlreadyExistsException entityAlreadyExistsException) // map duplicate name to 409
        {
            return Conflict(entityAlreadyExistsException.Message);
        }
        catch (Exception exception) // map any other errors to 500
        {
            return new StatusCodeResult(StatusCodes.Status500InternalServerError);
        }
    }

I would like to create a mapping for the controllers that catches exceptions and maps them to HTTP responses before sending them back to the client.

A similiar question has been asked 4 years ago

ASP.NET Core Web API exception handling

In NestJs it's possible to define your own mappings by extending a base class e.g.

export class MyCustomException extends HttpException {
  constructor() {
    super('My custom error message', HttpStatus.FORBIDDEN);
  }
}

Taken from here https://docs.nestjs.com/exception-filters#custom-exceptions

So basically I want to define mapping classes that could look like this sample code (this just shows my pseudo implementation)

// Map MyCustomException to NotFound
public class MyCustomExceptionMapping<TCustomException> : IExceptionMapping<TCustomException>
{
    public ActionResult Map(TCustomException exception)
    {
        return NotFound(exception.Message);
    }
}

Next I can cleanup the controller endpoint method to

public async Task<ActionResult<User>> UpdateUserUsernameAsync(/* DTOs */)
{
    User user = null; // Update user here

    return Ok(user);
}

Whenever an exception gets thrown the API would try to find the correct mapping interface. Otherwise it sends back a 500.

It would be nice to define such mappings and avoid a huge switch case for every exception in your project.

Does something like this exists? Is the accepted answer from the previous question still up to date?

Upvotes: 0

Views: 1413

Answers (1)

Shloime Rosenblum
Shloime Rosenblum

Reputation: 1020

Use Exception Filters it will be called when the controller throws an Exception and you can define the custom response. Microsoft Docs

public class MyExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        HttpStatusCode status;
        var message = "";

        var exceptionType = context.Exception.GetType();
        if (exceptionType is EntityNotFoundException)
        {
            status = HttpStatusCode.NotFound;
            message = context.Exception.Message;
        }
        else if (exceptionType is EntityAlreadyExistsException)
        {
            status = HttpStatusCode.Conflict;
            message = context.Exception.Message;
        }
        else
        {
           status = HttpStatusCode.InternalServerError;
           message = "Internal Server Error.";
        }

        //You can enable logging error

        context.ExceptionHandled = true;
        HttpResponse response = context.HttpContext.Response;
        response.StatusCode = (int)status;
        response.ContentType = "application/json";
        context.Result = new ObjectResult(new ApiResponse { Message = message, Data = null });
    }
}

To use the filter on all controllers you must register it in the ConfigureServices method in the Startup.cs

services.AddMvc(config =>
{
     config.Filters.Add(typeof(MyExceptionFilter));
})

Upvotes: 4

Related Questions