ValidationAttribute


Here are a few advantages of using ValidationAttribute for custom annotations in a RESTful API:


Separation of Concerns: By using validation attributes, the validation logic can be separated from the business logic. This makes the code cleaner and easier to understand.
Reusability: Custom validation attributes can be used multiple times across different classes or properties. This prevents code duplication and promotes the DRY (Don't Repeat Yourself) principle.
Declarative Programming: Applying an attribute to a property or class is more declarative than imperative. It clearly specifies how the validation should be done without cluttering the business logic.
Integration with Model Binding: In ASP.NET Core, model binding automatically applies these validation attributes during the model binding process.
Automatic Error Messages: A friendly error message is automatically added to the ModelState.Errors collection. These error messages can also be customized as per your needs.
Enforcing Business Rules: Custom validation attributes can enforce business rules at the data level.
IsValid method: Allows us to override the IsValid method where we define our custom validation logic.

Below is an example that demonstrates the ValidationAttribute implementation against a more object-oriented style just for reference.

public class CreateBookDto
{
private string _title { get; set; }
public string Title
{
get => _title;
set
{
if (value == Author)
{
throw new InvalidOperationException("Title and Author should not be having the same value");
}

_title = value;
}
}

private string _author { get; set; }
public string Author
{
get => _author;
set
{
if (value == _title)
{
throw new InvalidOperationException("Title and Author should not be having the same value");
}
_author = value;
}
}

[IsbnShouldNotBeTitle(nameof(Title), ErrorMessage = "ISBN should not be same as Title")]
public string Isbn { get; set; }
public int Copies { get; set; }
}
public class IsbnShouldNotBeTitle : ValidationAttribute
{
private readonly string _targetProperty;

public IsbnShouldNotBeTitle(string targetProperty)
{
_targetProperty = targetProperty;
}

protected override ValidationResult IsValid(object? value, ValidationContext validationContext)
{
var targetProperty = validationContext.ObjectType.GetProperty(_targetProperty);

if (targetProperty is null)
{
return new ValidationResult($"Property {_targetProperty} not found");
}

if (value is null)
{
return new ValidationResult($"You need to pass in a value to validate");
}

var targetValue = targetProperty.GetValue(validationContext.ObjectInstance, null);

if (targetValue != null && value.ToString() == targetValue.ToString())
{
return new ValidationResult(ErrorMessage);
}

return ValidationResult.Success!;
}
}

ValidationAttribute shines when we want to keep a tidy house to assist the team in focusing on business logic while we clearly communicate the rules.


No files yet, migration hasn't completed yet!