ASP.NET Core: Prevent Automatic HTTP 400 responses for individual action

I like the Automatic HTTP 400 responses functionality new to ASP.NET Core 2.1 and it’s working out really well for most cases.

However, in one action I need to do a bit of pre-processing before validation the payload. I have a custom validator that requires two values in the model to perform validation. One of those values is in the path so I would like to set that value on the model from the path then validate.

I don’t want to switch the functionality off for all actions with:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<ApiBehaviorOptions>(options =>
    {
        options.SuppressModelStateInvalidFilter = true;
    });
}

Is there any way I could switch it off just for an individual action?

Edit:

I tried modifying the InvalidModelStateResponseFactory but it didn’t solve my problem because I still need to get into the controller action:

services.Configure<ApiBehaviorOptions>(options =>
{
    options.InvalidModelStateResponseFactory = actionContext =>
    {
        var ignore = actionContext.ActionDescriptor.FilterDescriptors.Any(fd => fd.Filter is SuppressModelStateInvalidFilterAttribute);
        if (ignore)
        {
            // Can only return IActionResult so doesn't enter the controller action.
        }

        return new BadRequestObjectResult(actionContext.ModelState);
    };
});

[AttributeUsage(AttributeTargets.Method)]
public class SuppressModelStateInvalidFilterAttribute : FormatFilterAttribute
{
}

Edit:

Here’s a link to an issue I raised on the asp.net core repo in case I get anywhere with that – https://github.com/aspnet/Mvc/issues/8575

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

Update: you can just use the following code in ConfigureServices in Startup.cs:

services.Configure<ApiBehaviorOptions>(apiBehaviorOptions => {
    apiBehaviorOptions.SuppressModelStateInvalidFilter = true;
});

Based on Simon Vane’s answer, I had to modify the attribute for ASP.Net Core 2.2 as follows:

/// <summary>
/// Suppresses the default ApiController behaviour of automatically creating error 400 responses
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class SuppressModelStateInvalidFilterAttribute : Attribute, IActionModelConvention {
    private static readonly Type ModelStateInvalidFilterFactory = typeof(ModelStateInvalidFilter).Assembly.GetType("Microsoft.AspNetCore.Mvc.Infrastructure.ModelStateInvalidFilterFactory");

    public void Apply(ActionModel action) {
        for (var i = 0; i < action.Filters.Count; i++) {
            if (action.Filters[i] is ModelStateInvalidFilter || action.Filters[i].GetType() == ModelStateInvalidFilterFactory) {
                action.Filters.RemoveAt(i);
                break;
            }
        }
    }
}

Method 2

I had a response from Microsoft – https://github.com/aspnet/Mvc/issues/8575

The following worked a charm.

[AttributeUsage(AttributeTargets.Method)]
public class SuppressModelStateInvalidFilterAttribute : Attribute, IActionModelConvention
{
    public void Apply(ActionModel action)
    {
        for (var i = 0; i < action.Filters.Count; i++)
        {
            if (action.Filters[i] is ModelStateInvalidFilter)
            {
                action.Filters.RemoveAt(i);
                break;
            }
        }
    }
}

In my controller I could then make changes to the model before re-validating it (note the ModelState.Clear(), TryValidateModel add to existing model state):

if (model == null)
{
    return BadRequest(ModelState);
}

model.Property = valueFromPath;

ModelState.Clear();
if (TryValidateModel(model) == false)
{
    return BadRequest(ModelState);
}

Method 3

You could play with ApiBehaviorOptions.InvalidModelStateResponseFactory property to handle specific cases based on actionContext details:

services.Configure<ApiBehaviorOptions>(options =>
{
    options.InvalidModelStateResponseFactory = actionContext => 
    {
        // Do what you need here for specific cases with `actionContext` 
        // I believe you can cehck the action attributes 
        // if you'd like to make mark / handle specific cases by action attributes. 

        return new BadRequestObjectResult(context.ModelState);
    }
});

Method 4

This could probably be solved by implementing your own validator for your specific case. It is covered quite well in the documentation.

https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-2.1#custom-validation

Either that or possibly a custom model binder to create your model with all the preprocessing done before it is validated.

Method 5

I encountered similar problem and came up with this solution.

public class SuppressModelStateInvalidFilterAttribute : ActionFilterAttribute
{
    public SuppressModelStateInvalidFilterAttribute()
    {
        Order = -2500;
    }

    public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        context.ModelState.Clear();
        return next.Invoke();
    }
}


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x