Im trying to do a post request from Reactjs to ASP.NET API, using axios. At first, I got an 400 Bad request error. But after I changed the DTO in the backend, it worked, but the data receive is empty or null (I think this is one of those Model binding errors).
This is my React.Js file:
const defaultImgPath = '/assets/ImgStarter.jpg';
const initialFieldValues = {
productName: 'Fried spring rolls',
productPrice: 50000,
productStatus: 1,
productDescription: 'A short description',
imagePath: defaultImgPath,
imgName: '',
ProductImg: null
}
const ApiUploadImg = () => {
const [input, setInput] = useState(initialFieldValues)
const handleAddProduct = (e) => {
e.preventDefault();
axios.post('http://localhost:5273/api/product', input)
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error.response);
})
return (
<div className='form-uload'>
<form className='p-5' onSubmit={handleAddProduct}>
<Input
inputType="text"
labelname="Product name"
name="productName"
setInput={setInput}
input={input}
/>
<Input
inputType="number"
labelname="Price"
name="productPrice"
setInput={setInput}
input={input}
/>
<Input
inputType="text"
labelname="Status (Is available?)"
name="productStatus"
setInput={setInput}
input={input}
/>
<Input
inputType="text"
labelname="Description"
name="productDescription"
setInput={setInput}
input={input}
/>
<Input
inputType="file"
labelname="Image"
name="productImg"
setInput={setInput}
input={input}
onChange={handlePreviewImg}
/>
<div>
<img
src={input.imagePath}
className='preview-img'
/>
</div>
<button type='submit' className='form-controls btn btn-primary'>Submit</button>
</form>
</div>
)
}
My Input.js component:
const Input = ({ setInput, input, labelname, name, inputType, onChange=null}) => {
const [value, setValue] = useState('');
const onChangeHanlder = (e) => {
setInput({...input, [name]: e.target.value})
}
var id = {name};
return (
<Fragment>
<label htmlFor={id} className='font-semibold text-white'>
{labelname}:
</label>
<input
id={id}
type={inputType}
name={name}
className='rounded input_form form-control mt-2 w-100'
// placeholder={placeholder}
onChange={onChange ? onChange : onChangeHanlder}
/>
</Fragment>
)
}
My api function:
[HttpPost]
public async Task<IActionResult> Create([FromBody] CreateProductRequestDTO requestDTO)
{
if(!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var productModel = requestDTO.ToProductsFromCreateDTO();
await _productRepo.CreateAsync(productModel);
return CreatedAtAction(nameof(GetById), new {id = productModel.Id}, productModel.ToProductDTO());
}
And the main character, the DTO that I mentioned above. I really thought this would be the last place an error could happen, cause I literally copy paste the "name" attribute from the ReactJs file mentioned above and create this DTO (Solution (1)). But it didn’t work. So I paste the attributes from class Products, but as I said, this method make data received empty (Solution (2))
public class CreateProductRequestDTO
{
//Solution (1)
// public string? ProductName {get; set; } = string.Empty;
// public int ProductPrice {get; set; }
// public bool ProductStatus {get; set; }
// public string? ProductDescription {get; set; } = string.Empty;
//Solution (2)
public string Name {get; set; } = string.Empty;
public int Price {get; set; }
public bool Status {get; set; }
public string Description {get; set; } = string.Empty;
}
And in case you need it, the "input" object look exactly like "initialFieldValues"
Can someone explain it to me what is going on?
2
Answers
Try using FromForm.
If this doensn’t help, try give an example of the parsed call.
ModelBinding is part of the lifecycle of the request of .net
I am not sure if you are using .NetFramework or .net core, but the idea is the same, I will try to explain with .Net core
Your Controller is receiving a Dto that in this case because it is a POST request you are explicitly saying to read or to bind the parameters from the Body.
.Net core has a default binder that assigns all the values you are passing in your request based on a naming convention. For example, your Post request has this input which name is "productPrice" but in your DTO is Price
if you match the same naming convention it should work
Now, if you want to keep your react definition as it is and want to change the binding without changing your controller and your DTO, you can extend .Net core to register a model binder for you. I am going to update here only the Price so you can get the idea
This is an example:
}
Now you need to register the model binder and implement a modelbinder provider for your dto input and let .net core that everytime it sees your dto to use this model binder
finally in your dependency injection on .net core, insert your model binder as part of the default list of modelbinders, that gives you the flexibility to work with your request and don’t change the contracts you have already, so you have 2 options, follow the default convention and change the UI and pass the correct names, or register a model binder with your custom logic.