There are several ways in .NET 5 Web Api to bind request data into a model. Attributes such as [FromBody] or [FromRoute] can be used for this.
This works fine and is self-explaining. But there are cases where you will want both: A combination of route- and body-binding.
This can for example be the case with a PUT endpoint which will have the id in the route and additional properties in the request body.
Example:
Route: “api/users/{id}” → “api/users/1”
Body: “{ “name”: “John Doe”, “favoriteDish”: “Pizza” }”
Of course you could do something like this:
This will definitely work, but it has several disadvantages:
- You will have to map the route parameter manually
- If you use pre-controller model validation (e. g. with FluentValidation), the id will not be validated because it is empty at this point.
The next idea could be to define the source of every single property in the model like this:
Unfortunately, this will not work in .NET 5. After some research, I finally found the following working solution:
First you have to add the following line in Startup.cs → ConfigureServices:
Gets or sets a value that determines if model binding sources are inferred for
action parameters on controllers annotated with Microsoft.AspNetCore.Mvc.ApiControllerAttribute is suppressed.
When enabled, the following sources are inferred:Parameters that appear as route values, are assumed to be bound from the path.
Parameters of type Microsoft.AspNetCore.Http.IFormFile and Microsoft.AspNetCore.Http.IFormFileCollection are assumed to be bound from form.
Parameters that are complex are assumed to be bound from the body.
All other parameters are assumed to be bound from the query.
The next step is to change the model and endpoint implementation a bit like this:
See the approach in action:
You can find the repository with this example here.
If you have multiple cases where you need this behaviour, consider using a base model class, e. g. like this: