On an Asp.Net Core 5.0 project I have the ApiController
route and Model:
[HttpPost("users/{userId:int}/confirm-email")]
public async Task<IActionResult> ConfirmEmail(Model model) { }
public class Model {
[FromRoute] public Int32? UserId { get; set; }
[FromQuery] public String Token { get; set; }
}
Result: I got what I expected ...
UserId = 10
Token = "ABC"
I tried receive the token in the Body and not in the query parameter:
public class Model {
[FromRoute] public Int32? UserId { get; set; }
[FromBody] public String Token { get; set; }
}
In this case I got the error:
"errors": {
"$": [
"The JSON value could not be converted to System.String. Path: $ | LineNumber: 0 | BytePositionInLine: 1."
]
}
I used Insomnia.Rest to call the API and the body is:
{
"token": "ABC"
}
Then I tried to add [FromBody]
to Controller action:
[HttpPost("users/{userId:int}/confirm-email")]
public async Task<IActionResult> ConfirmEmail([FromBody]Model model) { }
public class Model {
[FromRoute] public Int32? UserId { get; set; }
[FromBody] public String Token { get; set; }
}
Result: I got the token from body but now userId is null.
UserId = null
Token = "ABC"
I have been trying a few variations but I can't figure out what is happening.
I also tried to call the API from Angular and the result is the same.
Any idea what am I missing?
By default, model binding gets data in the form of key-value pairs from the following sources in an HTTP request:
For each target parameter or property, the sources are scanned in the order indicated in the preceding list.
You can refer the following screenshot:
We can see that, the [FromRoute]
and the [FromForm]
attribute which apply to the property is not working. It uses the default inference rules to bind model.
Then, when the [FromBody]
attribute is applied to a complex type parameter (add the [FromBody]
attribute to Controller action), any binding source attributes applied to its properties are ignored. So, the UserId
is null. More detail information, see [FromBody]
attribute and Binding source parameter inference.
So, in this scenario, to access the value from route and request body, the easiest way is adding a parameter in the ConfirmEmail
method, code as below:
[HttpPost("users/{userId:int}/confirm-email")]
public async Task<IActionResult> ConfirmEmail(Model model, [FromRoute]int userid)
{
//set the userid
model.userId = userid;
return Ok("success");
}
The result as below:
Besides, you can also try to create a Custom model binder to bind the model.
Here are some relate articles, you can refer them: