ASP.NET Core Parameter Validation

In ASP.NET, you can validate your models by applying data annotations on the properties of the class. For example, Required and BindRequired are the most commonly used validations.

There are several ways to validate the values sent by users. The most commonly used methods are the validation attributes and the FluentValidation library.

Data annotations

You decorate your properties with the built-in annotations from the .NET or by creating your custom attributes.

public class Employee
{
    [BindRequired]
    public int Id { get; set; }
    [Required, StringLength(100)]
    public string Name { get; set; }

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

    [BindRequired, Range(typeof(DateTime), "1/1/2001", "1/1/2100")]
    public DateTime EmploymentDay { get; set; }

    [CompanyPosition(new string[] { "below", "above", "beside" })] 
    public string Position { get; set; }
}


public class CompanyPositionAttribute : ValidationAttribute 
{ 
    private string[] _allowedValues; 
  
    public CompanyPositionAttribute(string[] allowedValues) 
    { 
        _allowedValues = allowedValues; 
    } 
  
    protected override ValidationResult IsValid(object value, ValidationContext validationContext) 
    { 
        var position = value.ToString(); 
        if (_allowedValues.Contains(position.ToLower())) 
        { 
            return ValidationResult.Success; 
        } 
        return new ValidationResult($"{ position } is not a valid position"); 
    } 
}

Your model is validated on binding, then you should check the IsValid property of the ModelState ModelState.IsValid.

FluentValidation Library

FluentValidation is one of the most used libraries for strongly-typed validation. As the name of the library suggests, you can specify your validation rules in a fluent manner.

public class EmployeeValidator : AbstractValidator<Employee>
{
    public EmployeeValidator()
    {
        RuleFor(e => e.Id).NotEmpty();
        RuleFor(e => e.Name).NotEmpty().MaximumLength(100);
        RuleFor(e => e.Email).NotEmpty().EmailAddress(FluentValidation.Validators.EmailValidationMode.AspNetCoreCompatible).MaximumLength(100);
        RuleFor(e => e.EmploymentDay).NotEmpty().InclusiveBetween(new DateTime(2001, 1, 1), new DateTime(2100, 1, 1));
    }
}
[HttpPost]
public IActionResult Index(Employee employee)
{
    EmployeeValidator validator = new EmployeeValidator();

    ValidationResult result = validator.Validate(employee);
    if (!result.IsValid)
    {
        return BadRequest(ModelState);
    }
    return View();
}

Top-level node validation

From .NET Core 2.1 you can use top-level parameter validation. For example, if you only have one parameter that you get from the query string, it’s much easier to decorate the parameter than to create a class with one property.

[HttpPost("unix-time")]
public async Task<IActionResult> UnixTime([BindRequired, Range(0, 253402300799)] long timestamp)
{
    if (!ModelState.IsValid)
    {
        return BadRequest("Invalid timestamp");
    }

    var data = DateTimeOffset.FromUnixTimeSeconds(timestamp).UtcDateTime;
    return Json($"{data.ToLongDateString()} {data.ToLongTimeString()}");
}

I put the attribute near the parameter of the action. It is much cleaner for me to use top-level node validation than to create a class with a single property.

Leave a Comment