Isaac.

Model Binding in ASP.NET Core

Understand how ASP.NET Core binds request data to action parameters.

By EMEPublished: February 20, 2025
asp.net coremodel bindingdata bindingrouting

A Simple Analogy

Model binding is like an automatic mail sorter. It takes incoming request data and automatically places it into the right parameters.


Why Model Binding?

  • Automation: No manual parsing
  • Type safety: Automatic conversion
  • Validation: Built-in validation
  • Convenience: Less code
  • Flexibility: Multiple sources

Binding Sources

[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
    [HttpGet("{id}")]
    public ActionResult GetOrder(
        int id,                                    // From route
        [FromQuery] string status,                 // From query string
        [FromHeader] string authorization,        // From headers
        [FromBody] OrderFilter filter)             // From body
    {
        return Ok();
    }
    
    [HttpPost]
    public ActionResult Create(
        [FromForm] OrderDto orderData,             // From form
        [FromServices] IOrderService service)      // From DI
    {
        return CreatedAtAction(nameof(GetOrder), new { id = 1 });
    }
}

Custom Model Binder

public class DateTimeModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var modelName = bindingContext.ModelName;
        var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
        
        if (valueProviderResult == ValueProviderResult.None)
            return Task.CompletedTask;
        
        if (DateTime.TryParse(valueProviderResult.FirstValue, out var date))
        {
            bindingContext.Result = ModelBindingResult.Success(date);
        }
        else
        {
            bindingContext.ModelState.AddModelError(modelName, "Invalid date format");
        }
        
        return Task.CompletedTask;
    }
}

// Register
builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.ModelMetadataDetailsProviders.Add(
            new BindingSourceMetadataProvider(
                typeof(DateTime),
                new DateTimeModelBinder()));
    });

Complex Types

public class CreateOrderRequest
{
    public string CustomerId { get; set; }
    public List<OrderItem> Items { get; set; }
}

public class OrderItem
{
    public string ProductId { get; set; }
    public int Quantity { get; set; }
}

// Binds automatically
[HttpPost]
public ActionResult Create(CreateOrderRequest request)
{
    // request.Items are populated from JSON
    return Ok();
}

Validation

public class CreateOrderRequest
{
    [Required]
    [StringLength(50)]
    public string CustomerId { get; set; }
    
    [Required]
    [MinLength(1)]
    public List<OrderItem> Items { get; set; }
}

[HttpPost]
public ActionResult Create(CreateOrderRequest request)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }
    
    // request is valid
    return Ok();
}

Best Practices

  1. Use attributes: Make bindings explicit
  2. Validate early: Check ModelState
  3. Specify sources: Avoid ambiguity
  4. Document parameters: Swagger generation
  5. Test binding: Verify conversions work

Related Concepts

  • Dependency injection
  • Routing
  • Action filters
  • Validation

Summary

Model binding automatically converts request data into action parameters. Use FromRoute, FromQuery, FromBody, and FromForm attributes to control sources.