Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling error from binding action parameters to route values of incorrect types

I'm having troubles handling all types of errors in ASP.NET WebAPI.

I've successfully handled exceptions thrown inside my action methods using an ExceptionFilter and 404 errors for invalid routes, invalid controller or action name. However, I'm struggling to handle the error where the controller and action are both found, but the parameters for model binding are incorrect types.

Here's my action, which is routed to /api/users/{id}.

[HttpGet]
public virtual TPoco Get(long id)
{
    ...
}

If I request the URL /api/users/notinteger, I get a 400 Bad Request error that is handled outside of my code:

{
Message: "The request is invalid.",
MessageDetail: "The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int64' for method '___ Get(Int64)' in '___'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter."
}

How can I intercept this error and respond with my own error message? Preferably not in the controller itself as I'd like to handle several controllers in the same way.

I've tried using global.asax.cs's Application_Error event as per this question, but that doesn't seem to be called in this case.

like image 453
Connell Avatar asked Nov 10 '22 14:11

Connell


1 Answers

It appears that these errors are added to the ModelState as model binding errors. The action selector selects the correct action and the action invoker invokes it without throwing any errors.

The workaround I came up with is to create an action invoker that checks the ModelState for errors. If it finds any, it passes the first one to the exception handling method used by my ExceptionFilter and ErrorController.

internal class ThrowModelStateErrorsActionInvoker : ApiControllerActionInvoker
{
    public override Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
    {
        foreach (var error in actionContext.ModelState.SelectMany(kvp => kvp.Value.Errors))
        {
            var exception = error.Exception ?? new ArgumentException(error.ErrorMessage);
            //invoke global exception handling
        }

        return base.InvokeActionAsync(actionContext, cancellationToken);
    }
}

It's nasty, but it works. This has taken up most of my day and I'm just glad to have finally got somewhere.

I'd be interested to know what the consequences are to this. What else uses the ModelState errors in Web API? Could anyone add some possible flaws in this solution?

like image 194
Connell Avatar answered Nov 14 '22 23:11

Connell