I am trying to utilize FromRoute for one property of a model and FromBody for the remaining items as the API is accepting JSON. My code is as follows:
[HttpPost("{state_id}/StateContact")]
[Authorize]
[OktaAllowedRoles(["arms-dmcp-user", "arms-oact-user", "arms-approver"])]
[EnableRateLimiting("api")]
public async Task<IActionResult> AddStateContact(AddStateContactDTO addStateContactDTO)
{
try
{
return Ok(addStateContactDTO);
}
catch(Exception ex)
{
return Ok(ex);
}
}
and my model is as follows:
public class AddStateContactDTO
{
[FromRoute(Name = "state_id")]
public int StateId {get; set;}
[FromBody]
[ModelBinder(Name = "first_name")]
public string? FirstName {get; set;}
[FromBody]
[ModelBinder(Name = "last_name")]
public string? LastName {get; set;}
[FromBody]
[ModelBinder(Name = "email_address")]
[Required]
[EmailAddress(ErrorMessage = "The Email address submitted is not a valid e-mail address.")]
public required string EmailAddress {get; set;}
}
My request looks as follows:
const submitAddNewStateContact = () => {
if(addStateContactValid.value) {
const json = {
first_name : newStateContactFirstNameModel.value,
last_name : newStateContactLastNameModel.value,
email_address : newStateContactEmailAddressModel.value
}
let form = new FormData();
form.append('first_name' , JSON.stringify(newStateContactFirstNameModel.value))
form.append('last_name' , JSON.stringify(newStateContactLastNameModel.value))
form.append('email_address' , JSON.stringify(newStateContactEmailAddressModel.value))
api.post('/states/'+stateModel.value!.state_id+'/StateContact', json, config).then(response => {})
}
}
POST with the json data results in the following error:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"$": [
"The JSON value could not be converted to System.String. Path: $ | LineNumber: 0 | BytePositionInLine: 1.",
"The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true. Path: $ | LineNumber: 0 | BytePositionInLine: 0.",
"The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true. Path: $ | LineNumber: 0 | BytePositionInLine: 0."
],
"email_address": [
"The EmailAddress field is required."
]
},
"traceId": "00-bcbe8b4460713f9266f5d6bbc15b940a-1746bfdbf5ba5213-00"
}
However, If I were to turn my model into this:
[HttpPost("{state_id}/StateContact")]
[Authorize]
[OktaAllowedRoles(["arms-dmcp-user", "arms-oact-user", "arms-approver"])]
[EnableRateLimiting("api")]
public async Task<IActionResult> AddStateContact([FromForm] AddStateContactDTO addStateContactDTO)
{
try
{
return Ok(addStateContactDTO);
}
catch(Exception ex)
{
return Ok(ex);
}
}
public class AddStateContactDTO
{
[FromRoute(Name = "state_id")]
public int StateId {get; set;}
[ModelBinder(Name = "first_name")]
public string? FirstName {get; set;}
[ModelBinder(Name = "last_name")]
public string? LastName {get; set;}
[ModelBinder(Name = "email_address")]
[Required]
[EmailAddress(ErrorMessage = "The Email address submitted is not a valid e-mail address.")]
public required string EmailAddress {get; set;}
}
So basically switch to using FromForm and POSTing form data then everything binds as it should. However, I would like the option to send data as json without sending form data and also using FromRoute for some parameters of the model but this does not seem possible. Is this a bug or just not possible to use FromRoute and FromBody together?