Атрибуты валидации


Для валидации модели мы можем использовать большой набор встроенных атрибутов. Все эти атрибуты представляют классы, унаследованные от класса ValidationAttribute

Имеется довольно большое количество атрибутов. Основные из них:

Required: данный атрибут указывает, что свойство должно быть обязательно установлено, обязательно должно иметь какое-либо значение. Если свойство имеет значение null, то оно не проходит валидацию. Также не проходят валидацию свойства, которые представляют тип string, и которым присваивается пустая строка.

Из его свойств следует отметить свойство AllowEmptyStrings. Если оно имеет значение true, то для строковых свойств разрешено использовать пустые строки:

[Required(AllowEmptyStrings = true)]
public string? Name { get; set; } = "";
RegularExpression: указывает на регулярное выражение, которому должно соответствовать значение свойства. Например, пусть у пользователя определено свойство Phone, которое хранит номер телефона:

using System.ComponentModel.DataAnnotations;

CreateUser("+1111-111-2345"); // проходит валидацию
CreateUser("+11111112345"); // НЕ проходит валидацию
CreateUser("+0111-111-2345"); // НЕ проходит валидацию

void CreateUser(string phone)
{
User user = new User(phone);
var results = new List<ValidationResult>();
var context = new ValidationContext(user);
if (Validator.TryValidateObject(user, context, results, true))
Console.WriteLine("проходит валидацию");
else
Console.WriteLine("НЕ проходит валидацию");
}
public class User
{
[RegularExpression(@"^\+[1-9]\d{3}-\d{3}-\d{4}$")]
public string Phone { get; set; }

public User(string phone) => Phone = phone;
}
В этом случае номер телефона должен быть в формате "+xxxx-xxx-xxxx", например, +1111-111-2345.

StringLength: определяет допустимую длину для строковых свойств. В качестве первого параметра он принимает максимально допустимую длину строки. С помощью дополнительного свойства MinimumLength можно установить минимально допустимую длину строки

public class User
{
[StringLength(20, MinimumLength =3)]
public string Name { get; set; }

public User(string name) => Name = name;
}
В данном случае значение свойства Name должно иметь длину как минимум в 3 символа и максимум в 20 символов.

Range: задает диапазон допустимых числовых значений. В качестве первых двух параметров он принимает минимальное и максимальное значения:

[Range(1, 100)]
public int Age { get; set; }
Здесь значение свойства Age должно находиться в диапазоне от 1 до 100

Compare: позволяет сравнить значение текущего свойства со значением другого свойства, которое передается в этот атрибут.

Например, нередко при регистрации пользователя ему предоставляется два поля для ввода пароля, чтобы не ошибиться. В этом случае мы могли бы определить ля регистрации следующий класс:

public class User
{
[Required]
public string Name { get; set; }

[Required]
public string? Password { get; set; }

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

public User(string name) => Name = name;
}
И если значения свойств Password и ConfirmPassword не будут совпадать, тогда мы получим ошибку валидации.

Phone: данный атрибут автоматически валидирует значение свойства, является ли оно телефонным номером. Фактически это встроенная альтернатива использованию регулярного выражения, как было показано выше

[Phone]
public string? Phone { get; set; }
EmailAddress: определяет, является ли значение свойства электронным адресом

CreditCard: определяет, является ли значение свойства номером кредитной карты

Url: определяет, является ли значение свойства гиперссылкой

Настройка сообщения об ошибке
Класс ValidationAttribute определяет для атрибутов ряд общих свойств и методов, из которых следует выделить свойство ErrorMessage. Это свойство позволяет задать сообщение обошибке.

При выводе ошибок валидации .NET использует встроенные локализованные сообщение. А данное свойство как раз и позволяет переопределить сообщение об ошибке:

using System.ComponentModel.DataAnnotations;

CreateUser("Tom", 37);
CreateUser("T", 120);
CreateUser("", -2);

void CreateUser(string name, int age)
{
User user = new User(name, age);
var results = new List<ValidationResult>();
var context = new ValidationContext(user);
if (!Validator.TryValidateObject(user, context, results, true))
{
foreach (var error in results)
{
Console.WriteLine(error.ErrorMessage);
}
}
else
Console.WriteLine($"Объект User успешно создан. Name: {user.Name}");

Console.WriteLine(); // для разделения
}
public class User
{
[Required(ErrorMessage = "Не указано имя пользователя")]
[StringLength(50, MinimumLength = 3, ErrorMessage = "Недопустимая длина имени")]
public string Name { get; set; }

[Range(1, 100, ErrorMessage = "Недопустимый возраст")]
public int Age { get; set; }

public User(string name, int age)
{
Name = name;
Age = age;
}
}
Например, возьмем свойство Name:

[Required(ErrorMessage = "Не указано имя пользователя")]
[StringLength(50, MinimumLength = 3, ErrorMessage = "Недопустимая длина имени")]
public string Name { get; set; }
Если для свойства Name будет передана пустая строка, то будет выводиться сообщение об ошибке "Не указано имя пользователя". Если же длина имени меньше 2 или больше 50 символов, то выводится сообщение "Недопустимая длина имени".

В итоге в данном случае мы получим следующий консольный вывод:

Объект User успешно создан. Name: Tom

Недопустимая длина имени
Недопустимый возраст

Не указано имя пользователя
Недопустимый возраст
Передача в сообщение об ошибке аргументов
Ряд атрибутов с помощью свойств могут принимать различные параметры. И эти параметры можно использовать в сообщении об ошибке. Например, изменим предыдущий код следующим образом:

using System.ComponentModel.DataAnnotations;

CreateUser("T", 120);

void CreateUser(string name, int age)
{
User user = new User(name, age);
var results = new List<ValidationResult>();
var context = new ValidationContext(user);
if (!Validator.TryValidateObject(user, context, results, true))
{
foreach (var error in results)
{
Console.WriteLine(error.ErrorMessage);
}
}
}
public class User
{
[Required(ErrorMessage = "Не указано имя пользователя")]
[StringLength(50, MinimumLength = 3, ErrorMessage = "Длина имени должна быть в диапазоне от {2}-{1} символов")]
public string Name { get; set; }

[Range(1, 100, ErrorMessage = "Возраст должен быть в диапазоне {1}-{2}")]
public int Age { get; set; }

public User(string name, int age)
{
Name = name;
Age = age;
}
}
Например, возьмем свойство Name и его атрибут StringLength:

[StringLength(50, MinimumLength = 3, ErrorMessage = "Длина имени должна быть в диапазоне от {2}-{1} символов")]
public string Name { get; set; }
В сообщение об ошибке с помощью плейсхолдеров {0}, {1}, {2} и т.д. можно передавать параметры атрибута. Параметр {0} в любом атрибуте представляет имя свойства, которое валидируется. То есть в данном случае это было бы "Name". {1} здесь - первый параметр атрибута - максимальное количество символов - 50. {2} - второй параметр - значение свойства MinimumLength. То есть в итоге сформируется сообщение Длина имени должна быть в диапазоне от 3-50 символов

Аналогично для свойство Age и его атрибута Range:

[Range(1, 100, ErrorMessage = "Возраст должен быть в диапазоне {1}-{2}")]
public int Age { get; set; }
Здесь параметр {0} представлял бы имя свойства - "Age". {1} здесь - первый параметр атрибута - минимальное значение - 1. {2} - максимальное значение 100.

И в данном случае мы получим следующий консольный вывод:

Длина имени должна быть в диапазоне от 3-50 символов
Возраст должен быть в диапазоне 1-100