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

C# Models/Product.cs
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:

C# Controllers/ProductsController.cs
[HttpPost]
public IActionResult Create([FromBody] Product product)
{
    return Ok($"Created {product.Name} at ${product.Price}");
}
Terminal
$ 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:

C# Models/Product.cs
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; }
}
Terminal — sending an empty name
$ 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.

The silent-null gotcha: without [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.