Skip to main content

Model Binding and Validation

Level: Intermediate

ℹ️ What You'll Learn
  • Model binding definition: Automatically convert HTTP request data (JSON body, URL, query) into C# objects
  • [FromBody] binding: POST/PUT requests send JSON → automatically converts to Student object
  • [FromRoute] binding: URL path parameters automatically converted to action method parameters
  • [FromQuery] binding: Query string ?name=Ravi → converts to parameter string name
  • Data annotation validators: [Required], [StringLength(100)], [Range(1, 12)], [EmailAddress], [RegularExpression]
  • [Required]: Field mandatory, returns 400 if missing
  • [StringLength(max)]: Max character length, [StringLength(100, MinimumLength=3)] requires 3-100 chars
  • [Range(min, max)]: Numeric range, [Range(1, 12)] for class number 1-12
  • [RegularExpression(pattern)]: Regex validation (roll number pattern SMS-2024-001)
  • [EmailAddress]: Email format validation
  • ModelState.IsValid: Check if all validation passed, return 400 BadRequest if not
  • Validation error response: 400 Bad Request with field errors listing which constraints failed
  • Custom validation: Implement IValidatableObject for complex validation (e.g., endDate > startDate)
  • School Management validation: Student name required, roll number format SMS-YYYY-NNN, class 1-12, email valid
  • Fluent Validation: Alternative library with cleaner syntax than attributes for complex rules
  • Error display: Validation messages shown to user (what field is wrong, what constraint failed)

Model Binding

Automatic conversion of request data to C# objects:

[HttpPost]
public async Task<ActionResult<Student>> CreateStudent([FromBody] Student student)
{
// student automatically populated from request body
// { "name": "Ravi", "className": "10-A" }
}

Binding sources:

  • [FromBody] — Request body (JSON)
  • [FromRoute] — URL path parameters
  • [FromQuery] — Query string (?name=value)
  • [FromHeader] — HTTP headers
  • [FromForm] — Form data

Student Model with Validation

File: Models/Student.cs

public class Student
{
[Required]
public int Id { get; set; }

[Required]
[StringLength(100, MinimumLength = 3)]
public string Name { get; set; }

[Required]
[RegularExpression(@"^SMS-\d{4}-\d{3}$")]
public string RollNumber { get; set; }

[Required]
[Range(1, 12)]
public int ClassNumber { get; set; }

[EnumDataType(typeof(StudentStatus))]
public StudentStatus Status { get; set; }

[EmailAddress]
public string Email { get; set; }

[Range(typeof(DateTime), "1990-01-01", "2020-12-31")]
public DateTime DateOfBirth { get; set; }
}

public enum StudentStatus { Active, Inactive, Graduated }

Validation Attributes

AttributeValidation
[Required]Field mandatory
[StringLength(max)]Max string length
[Range(min, max)]Numeric range
[EmailAddress]Valid email
[RegularExpression(pattern)]Regex pattern
[Compare(property)]Compare two fields
[EnumDataType(enum)]Valid enum value

Automatic Validation

[HttpPost]
public async Task<ActionResult<Student>> CreateStudent([FromBody] Student student)
{
if (!ModelState.IsValid)
return BadRequest(ModelState); // 400 with errors

await _service.CreateStudentAsync(student);
return CreatedAtAction(nameof(GetStudent), new { id = student.Id }, student);
}

[ApiController] automatically returns 400 for invalid models.

Validation Response

Invalid request:

{
"errors": {
"Name": ["The Name field is required."],
"ClassNumber": ["The field ClassNumber must be between 1 and 12."]
},
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400
}

Fluent Validation (Optional)

Advanced validation:

using FluentValidation;

public class StudentValidator : AbstractValidator<Student>
{
public StudentValidator()
{
RuleFor(x => x.Name).NotEmpty().MinimumLength(3);
RuleFor(x => x.RollNumber).Matches(@"^SMS-\d{4}-\d{3}$");
RuleFor(x => x.ClassNumber).InclusiveBetween(1, 12);
RuleFor(x => x.DateOfBirth).LessThan(DateTime.Now.AddYears(-5));
}
}

// Register in Program.cs
builder.Services.AddValidatorsFromAssemblyContaining<StudentValidator>();

Query Parameter Validation

[HttpGet]
public async Task<ActionResult<List<Student>>> GetStudents(
[FromQuery] int? classNumber, // Optional
[FromQuery] string status) // Optional
{
// classNumber validated as int
// status validated as string
}

// Request: GET /api/students?classNumber=10&status=Active

Key Takeaways

  • Model binding = automatic data conversion
  • Validation = prevent invalid data
  • Attributes = simple validation
  • FluentValidation = complex rules
💡 Validation Tip

Validate on API boundary. Never trust client data.

🤖Use AI to Learn Faster

Use ChatGPT, Claude, or Copilot to go deeper on Model Binding and Validation. Try these prompts:

  • "What's the difference between [FromBody] and [FromQuery]?"
  • "When should I use FluentValidation?"
  • "How do I return validation errors?"
  • "Quiz me on validation"

💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.

nexcoding.in