ASP.Net Core required parameter binding fails to fail using FromBody

883 Views Asked by At

I'm developing an Asp.Net Core API.

My controller declaration

[ApiController]
public class BarController : Controller
{
    ...
}

My endpoint looks like this

[HttpPost, Route("bars")]
public async Task<ActionResult> DoAsync(
    [FromBody] UpdateBars command)
{
    // Do something with the command
    return Ok(result);
}

The command looks like this

public class UpdateBars
{
    [Required]
    public IEnumerable<string> Ids { get; set; }

    // ... more properties
}

Compatibility level is set to 2.1

public IServiceProvider ConfigureSharedServices(IServiceCollection services)
{
    // ...
    services.AddMvc()
        .AddControllersAsServices()
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    // ...
}

Old question: I'd expect this to return a 400 bad request with a missing Ids parameter, but it fails to return the binding error. What am I doing wrong?

Updated question: I'd expect this to return a 400 bad request with a missing or empty Ids parameter. The response is as expected if the parameter is missing (null), but returns 200 ok if it is an empty collection. Is it possible to change something so that I get a bad request when the parameter is present but empty?

2

There are 2 best solutions below

2
On

You should add the [ApiController] attribute. In that case, an automatic HTTP 400 response containing error details is returned when model state is invalid. For more information, see Automatic HTTP 400 responses. Automatic HTTP 400 responses.

1
On

You probably didn't put a [ApiController] attribute on your controller.

By default the validation is not 'automatic'. If you don't want to put that attribute on your controller, you will have to validate the model yourself, in following way:

[HttpPost, Route("bars")]
public async Task<ActionResult> DoAsync(
    [FromBody] UpdateBars command)
{
    if (!ModelState.IsValid)
    {
       return BadRequest(ModelState);
    }
    // Do something with the command
    return Ok(result);
}

You have more controll in that way, but if you just need to return a BadRequest with the model state, it will happen automatically if you put the [ApiController] on the controller.

Also in that case it will mark all action parameters as [FromBody], so putting that attribute on params is not needed