Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling Exceptions in a Base Controller with ASP.net core (API controller)

Many of our current controllers look like this:

[HttpPost]
public List<Foo> Post([FromBody]Bar model)
{
    if (model == null)
    {
        throw new ArgumentNullException();
    }

    try
    {
        // business logic
    }
    catch (Exception ex)
    {
        // logging
    }

    return dto;
}

A lot of code is being repeated here though. What I'd like to do is implement a base controller that handles exceptions so I can return a standardized response with fields like Payload, Success, Error, etc.

Prior to .net core this was possible by providing an override of OnException however this doesn't appear to work with a .net core api controller. How do I go about consolidating this exception logic to return a custom response when things go awry in my controller bodies?

I'd like this, as a starting point:

[HttpPost]
public StandardFoo Post([FromBody]Bar model)
{
    if (model == null)
    {
        throw new ArgumentNullException();
    }

    // business logic

    return new StandardFoo(){Payload: dto};
}

Where exceptions thrown by model validation or business logic bubble up to some piece of logic that returns a new StandardFoo with a property containing the exception details.

like image 667
BLAZORLOVER Avatar asked Oct 18 '17 15:10

BLAZORLOVER


2 Answers

If shortly, you should not catch and process exceptions in your controllers.


Instead, you need to separate normal and error flows in your code and then process error flow separately. One of the main approaches to indicate that normal flow is not possible is to raise the .NET Exceptions (and you use it). But:

  • Controllers actions should be aware only of normal flow. No try-catch logic and so on.
  • For input validation use ActionFilter. You may have global filters for all controllers or define specific per action. See Filters section in documentation. ASP.NET Core allows also do Model Validation.

  • During controller action execution you should raise exceptions as soon as possible and stop further pipeline execution. And yes, the Exception may be raised on any of the levels (action level, Service/Business layer, DA layer, etc).

How to handle the raised exception then?

  • use provided by ASP.NET Core error handling approaches (like ExceptionHandler, or Exception Filters), it allows to analyze exceptions and generate appropriate/different responses accordingly. Look into related SO Error handling in ASP.NET Core question for the example. There is also the error-handling section in documentation.
like image 91
Set Avatar answered Oct 17 '22 06:10

Set


I would recommend creating a custom action filter. This can be wrapped around every incoming request in the WebApiConfig Register method(See below).

In my example, I am checking that the model state is valid.

If it's not, I create an ErrorResponse and send back a Bad Request.

You don't have to simply send back the model state like in the example below, you could return anything you actually want.

This way it becomes uniform across all endpoints that have a model that needs to be validated as well as any other checks you want to do at this point in the pipeline.

Note: Because we are registering this attribute globally we dont then have to declare it anywhere else, from this point on, all incoming traffic be inspected by this class.

   public class ValidateModelAttribute : ActionFilterAttribute
        {
            public override void OnActionExecuting(HttpActionContext actionContext)
            {

                if (!actionContext.ModelState.IsValid)
                {
                    actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
                }
            }

            public override bool AllowMultiple
            {
                get { return false; }
            }
        }



public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services
            config.Filters.Add(new ValidateModelAttribute());
        }
    }
like image 33
Derek Avatar answered Oct 17 '22 05:10

Derek