ASP.NET Core  

Model Validation in ASP.NET Core Using Data Annotations

Model validation is one of the most important parts of building secure and reliable web applications. It protects your application from invalid data, accidental mistakes, and even malicious inputs. In ASP.NET Core, model validation becomes much simpler with the help of Data Annotations. These small attributes, which you place on your model properties, allow you to define validation rules quickly without writing long validation logic manually.

In this article, we will explore model validation in ASP.NET Core using data annotations. We will break down how it works, why it matters, the most common validation attributes, how to create custom validation rules, and how validation interacts with controllers, Razor pages, and APIs. Everything will be explained in simple language so you can apply it to your project immediately.

What Is Model Validation?

Model validation is the process of checking whether the data sent by a user or an API client meets the rules you expect. For example, if your form asks for an email, you must ensure the email is valid. If your application requires a password of at least eight characters, you want to reject a short password. If an API endpoint receives a product with a price, you need to ensure the price is a positive number.

In ASP.NET Core, validation can happen automatically. Once ASP.NET Core sees the data mapped into a model, it checks if that model is valid. If it is not valid, the framework reports the validation errors so you can return the right message to the user.

What Are Data Annotations?

Data Annotations are simple attributes that you place above class properties (or sometimes classes). These attributes contain rules describing how the data should be validated. Examples include:

  • Required

  • StringLength

  • MaxLength

  • Range

  • EmailAddress

  • Compare

  • RegularExpression

By adding these annotations, ASP.NET Core automatically validates the model when it is submitted.

For example:

public class RegisterViewModel
{
    [Required]
    public string Username { get; set; }

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

    [Required]
    [MinLength(8)]
    public string Password { get; set; }
}

With this code, ASP.NET Core will reject any user registration that has missing or invalid fields before your controller even runs your logic.

Why Model Validation Matters

Model validation protects your application. Here are the most important reasons:

1. Prevents Wrong Data From Entering Your System

Without validation, users may submit incomplete, incorrect, or badly formatted data. This could lead to database errors, application crashes, or corrupted data.

2. Improves User Experience

With proper validation, users get clear error messages about what is wrong. This allows them to fix mistakes easily.

3. Security

Validation is essential for preventing attacks like SQL injection, cross-site scripting, and overposting attacks. When the incoming data is validated and restricted, attackers have fewer ways to break your system.

4. Saves Developer Time

Instead of writing your own validation logic every time, you can use built-in Data Annotations. They reduce code duplication and make your system easier to maintain.

How Model Validation Works in ASP.NET Core

To understand Data Annotations better, you need to know how the ASP.NET Core pipeline handles model validation.

Step 1. User Sends Data

This could be form data from a Razor page or JSON data from a web API.

Step 2. ASP.NET Core Model Binding

The framework maps the data to your model class. For example, name, email, password, and other inputs will be mapped to the model properties.

Step 3. Validation Automatically Runs

ASP.NET Core checks all the Data Annotation attributes on the model.

Step 4. Controller Receives the Data

If the model is invalid, the ModelState object in the controller will contain errors.

You check it like this:

if (!ModelState.IsValid)
{
    return View(model);
}

In APIs:

if (!ModelState.IsValid)
{
    return BadRequest(ModelState);
}

This simple pattern allows you to safely work with valid data.

Common Data Annotation Attributes

ASP.NET Core provides many built-in attributes. Below are the most commonly used ones with simple explanations.

1. Required

Marks a property as mandatory.

[Required]
public string FirstName { get; set; }

2. StringLength

Sets minimum and maximum allowed length for a string.

[StringLength(50, MinimumLength = 3)]
public string Username { get; set; }

3. MaxLength and MinLength

These specify length limits without affecting database schema.

[MaxLength(200)]
public string Description { get; set; }

4. Range

Used to specify numeric limits.

[Range(1, 100)]
public int Quantity { get; set; }

5. EmailAddress

Validates email format.

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

6. Phone

Validates phone numbers.

[Phone]
public string PhoneNumber { get; set; }

7. Url

Validates website URLs.

[Url]
public string Website { get; set; }

8. Compare

Useful for confirming fields, such as password confirmation.

[Compare("Password")]
public string ConfirmPassword { get; set; }

9. RegularExpression

Powerful for custom pattern validation.

[RegularExpression(@"^[A-Za-z0-9]{5,10}$")]
public string Code { get; set; }

10. CreditCard

Validates credit card numbers.

[CreditCard]
public string CreditCardNumber { get; set; }

Custom Error Messages

Each attribute allows custom error messages. This gives users clearer feedback.

[Required(ErrorMessage = "Email is required.")]
[EmailAddress(ErrorMessage = "Enter a valid email address.")]
public string Email { get; set; }

This is important for improving user experience and creating more helpful forms.

Server-Side vs Client-Side Validation

ASP.NET Core supports both server-side and client-side validation.

Server-Side Validation

This runs on the server when data is submitted. It is always required because client-side validation can be bypassed.

You check it using:

if (!ModelState.IsValid)
{
    return View(model);
}

Client-Side Validation

Browsers can show validation errors instantly without sending a request to the server. ASP.NET Core uses jQuery Validation for this by default in MVC and Razor Pages.

You must include these scripts:

<script src="~/lib/jquery/jquery.js"></script>
<script src="~/lib/jquery-validation/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>

Client-side validation improves usability, but server-side validation is the true protection.

Validation in MVC Controllers

When using MVC, validation typically happens inside POST actions.

Example

[HttpPost]
public IActionResult Register(RegisterViewModel model)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }

    // Process valid registration
    return RedirectToAction("Success");
}

The controller action receives the model, and ASP.NET Core has already filled it with any validation errors.

Validation in Razor Pages

Razor Pages also support validation automatically.

Example

public class RegisterModel : PageModel
{
    [BindProperty]
    public RegisterViewModel Input { get; set; }

    public IActionResult OnPost()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        return RedirectToPage("Success");
    }
}

Razor Pages work similarly to MVC but without needing separate controllers.

Validation in ASP.NET Core Web API

For APIs, model validation works a bit differently. API methods usually return JSON responses instead of web views.

Simple example

[HttpPost]
public IActionResult CreateProduct(ProductModel model)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    return Ok("Product created");
}

Automatic 400 Responses

In ASP.NET Core 2.1 and later, the framework can automatically return a 400 Bad Request response when the model is invalid. You can enable or disable this behavior.

Binding and Overposting Protection

Model validation also helps protect against overposting attacks. An overposting attack happens when a client sends data for properties they should not be allowed to change.

Example: A user trying to set IsAdmin = true in their profile.

To prevent this, always use a ViewModel or DTO with specific properties instead of binding entire database model classes.

Creating Custom Validation Attributes

Sometimes built-in attributes are not enough. ASP.NET Core allows you to create your own validation attributes by extending ValidationAttribute.

Example: A simple custom validator that checks if the value is not a future date.

public class NotFutureDateAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        if (value == null)
            return true;

        DateTime date = (DateTime)value;
        return date <= DateTime.Now;
    }
}

Using it:

[NotFutureDate(ErrorMessage = "Date cannot be in the future.")]
public DateTime BirthDate { get; set; }

Custom validation gives you total control over data rules.

Cross-Field Validation Using IValidatableObject

If you need to validate multiple fields together, you can use IValidatableObject.

Example: Ensuring end date is greater than start date.

public class EventModel : IValidatableObject
{
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext context)
    {
        if (EndDate < StartDate)
        {
            yield return new ValidationResult(
                "End date must be after start date.",
                new[] { "EndDate" });
        }
    }
}

This is useful for business rules that depend on multiple properties.

Display Attributes

Data Annotations also include display attributes that improve how validation messages and form labels appear.

Example

[Display(Name = "Full Name")]
public string Name { get; set; }

This makes forms easier for users to understand.

Validation Summary and Field Errors in Views

In Razor views, you show validation messages using:

@Html.ValidationSummary()
@Html.ValidationMessageFor(m => m.Email)

Or in Razor Pages:

<partial name="_ValidationSummary" />

These helpers display error messages generated from Data Annotations.

Handling Validation Errors in APIs

When validation fails in APIs, you often return JSON with error details.

Example response

{
  "Email": [
    "Email is required.",
    "Enter a valid email address."
  ]
}

You can customize error messages or create a standard API error format.

Validation Best Practices

To use Data Annotations effectively, follow these best practices.

1. Use ViewModels Instead of Entities

Never apply validation directly on Entity Framework models for user input. Use a ViewModel or DTO layer.

2. Combine Client-Side and Server-Side Validation

Client-side validation improves user experience.
Server-side validation ensures security.

3. Use Custom Attributes Only When Needed

Do not write custom validation unless the built-in attributes cannot handle your case.

4. Keep Error Messages Clear

Good error messages make your application more user friendly.

5. Unit Test Your Validation

You can write tests to make sure your custom validators behave correctly.

Real-World Example: User Registration Form

Here is a practical example combining everything.

Model

public class RegisterViewModel
{
    [Required(ErrorMessage = "Username is required.")]
    [StringLength(20, MinimumLength = 3)]
    public string Username { get; set; }

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

    [Required]
    [MinLength(8)]
    public string Password { get; set; }

    [Compare("Password")]
    public string ConfirmPassword { get; set; }
}

Controller

[HttpPost]
public IActionResult Register(RegisterViewModel model)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }

    return RedirectToAction("Success");
}

This is a clean and maintainable way to validate registration data.

Conclusion

Model validation is a core part of building safe, stable, and user-friendly ASP.NET Core applications. Data Annotations provide a simple but powerful way to define validation rules directly on your models. They remove the need for repetitive code, reduce bugs, and make your application easier to maintain.

With built-in validators like Required, Range, and EmailAddress, plus support for custom validation and cross-field checks, ASP.NET Core gives you all the tools you need to validate user input effectively. Whether you are building MVC apps, Razor Pages, or APIs, validation works the same way and helps you keep your system secure.

By understanding and applying the techniques in this article, you can build web applications that accept only valid, safe, and well-structured data.