Custom Validation Attributes

Create custom validation attributes in ASP.NET Core

Custom Validation Attributes

1. Introduction

It describes the why and how to create custom validation attributes. For details refer to the official documentation of Model Validation in ASP.NET Core MVC and Razor Pages.

2. Why do we need them?

We need custom validation attributes for scenarios that the built-in validation attributes don't handle. Custom validation attributes are a powerful way to enforce the rules in our ASP.NET Web APIs.

A few benefits of using custom validation attributes:

  • They help in improving the security of the web API.

  • They make sure that the data that is submitted to the web API is valid.

  • They make your code reusable - as the validation logic is written once and then can be used any number of times.

3. Naming them

The naming of custom validation attributes is an important aspect because it helps to communicate the purpose of the attribute to other developers without, them needing to dig into its logic.

Consider, we have to create a custom validation attribute that checks if the value is greater than 0. The possible names of the attributes are:

  • GreaterThanZeroValidationAttribute

  • PositiveIntegerValidationAttribute

  • NaturalNumberValidationAttribute

These names are descriptive that accurately reflect the purpose of the attribute. They are also short and do not contain any abbreviations or acronyms. I will choose GreaterThanZeroValidationAttribute as the name effortlessly conveys its purpose to the reader.

4. How to create one?

Create a class GreaterThanZeroValidationAttribute that inherits from ValidationAttributes and override its IsValid method.

The IsValid method accepts an object named value, which is the input to be validated. An overload also accepts a ValidationContext object, which provides additional information, such as the model instance created by model binding.

[AttributeUsage(AttributeTarget.Property | AttributeTarget.Parameter)]
public class GreaterThanZeroValidationAttribute : ValidationAttribute
{
    protected override ValidationResult? IsValid(Object? value, ValidationContext validationContext)
    {
        if (value is Int32 id && id > 1)
            return ValidationResult.Success;

        var errorMessage = String.Format("{0} must be greater than 0.", validationContext.MemberName!);
        var result = new ValidationResult(errorMessage);

        return result;
    }
}

AttributeUsage controls how the custom validation attribute class is used. For details visit AttributeUsageAttribute Class.

AttributeTarget.Property | AttributeTarget.Parameter states what the class can target - properties & parameters, in this case. If [GreaterThanZeroValidation] is used on anything else, it will produce a compilation error. For details visit AttributeTarget Enum.

5. Usage

5.1 When the target is a parameter

OrderController.cs

[ApiController]
[Route("api/[controller]")]
public class OrderController : ControllerBase
{
    [HttpGet("{id}")]
    public async Task<IActionResult> GetByIdAsync([GreaterThanZeroValidation] Int32 id)
    {
        //fetch the order using id, asynchronously.
    }
}

5.2 When the target is a property of a class

OrderViewModel.cs

public class OrderViewModel
{
    [GreaterThanZeroIntegerValidation]
    public Int32 Id { get; set; }

    public Dictionary<Int32, Int32> ProductQuantityMap { get; set; } = null!;
}

OrderController.cs

[HttpPost]
public async Task<IActionResult> CreateAsync(OrderViewModel viewModel)
{
    //Do something with the viewModel.
}