Models & Model Binding
A model is just a plain C# class describing a shape of data. Model binding is ASP.NET automatically filling one of those classes from an incoming request — JSON body, form fields, route values, or query string — so your action method receives a ready-to-use object instead of raw strings.
Defining a model
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = "";
public decimal Price { get; set; }
}
Binding a JSON request body
[FromBody] tells ASP.NET to deserialize the incoming JSON body directly into the parameter:
[HttpPost]
public IActionResult Create([FromBody] Product product)
{
return Ok($"Created {product.Name} at ${product.Price}");
}
$ curl -X POST http://localhost:5225/api/products \
-H "Content-Type: application/json" \
-d '{"name":"Webcam","price":39.99}'
"Created Webcam at $39.99"Notice the request JSON used lowercase name/price while the C# class has Name/Price — ASP.NET's default JSON binding matches property names case-insensitively, so this works without any extra configuration.
Automatic validation with [ApiController]
Adding data annotations to a model and combining them with [ApiController] (from lesson 3) gets you free validation — a request that fails validation never even reaches your method body:
public class Product
{
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; } = "";
[Range(0.01, 10000)]
public decimal Price { get; set; }
}
$ curl -i -X POST http://localhost:5225/api/products \
-H "Content-Type: application/json" \
-d '{"name":"","price":9.99}'
HTTP/1.1 400 Bad Request
{"errors":{"Name":["The Name field is required."]}}The controller's Create method above never runs at all for this request — [ApiController] short-circuits with a 400 automatically as soon as model validation fails.
[Required], a missing JSON property doesn't cause an error — it just leaves the C# property at its default value (null for a string, 0 for a number). A typo in the incoming JSON's field name ("nmae" instead of "name") fails exactly the same way: no error, just a silently empty property. Validation attributes are what turn that into a visible 400 instead of a quiet bug downstream.