Ответ
Да, постоянно. Валидация — критически важный слой для безопасности и целостности данных. В ASP.NET Core применяю многоуровневый подход.
1. Валидация на уровне модели (Model Validation)
Использую атрибуты из пространства имён System.ComponentModel.DataAnnotations для декларативного описания правил.
public class UserRegistrationDto
{
[Required(ErrorMessage = "Имя пользователя обязательно.")]
[StringLength(50, MinimumLength = 3)]
public string Username { get; set; }
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
[RegularExpression(@"^(?=.*[a-z])(?=.*[A-Z])(?=.*d).{8,}$",
ErrorMessage = "Пароль должен содержать минимум 8 символов, заглавную и строчную буквы, цифру.")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Compare(nameof(Password), ErrorMessage = "Пароли не совпадают.")]
public string ConfirmPassword { get; set; }
[Range(18, 120)]
public int Age { get; set; }
}
В контроллере проверка автоматическая:
[HttpPost]
public IActionResult Register(UserRegistrationDto model)
{
if (!ModelState.IsValid) // Автоматическая проверка атрибутов
{
// Возвращаем ошибки клиенту (вместе с состоянием модели для формы)
return BadRequest(ModelState);
}
// Логика регистрации...
}
2. Сложная бизнес-логика с FluentValidation
Для правил, зависящих от контекста или нескольких полей, предпочитаю библиотеку FluentValidation.
public class UserRegistrationValidator : AbstractValidator<UserRegistrationDto>
{
public UserRegistrationValidator(IUserRepository repository)
{
RuleFor(x => x.Username)
.MustAsync(async (name, cancellation) =>
{
return !await repository.UserExistsAsync(name);
})
.WithMessage("Имя пользователя уже занято."); // Кастомная асинхронная проверка
RuleFor(x => x.Age)
.Must(age => age >= 18).When(x => x.Email.EndsWith(".ru"))
.WithMessage("Для российских пользователей возраст должен быть 18+"); // Условная валидация
}
}
// Регистрация валидатора в DI-контейнере
services.AddScoped<IValidator<UserRegistrationDto>, UserRegistrationValidator>();
3. Валидация в сервисном слое
Атрибуты не покрывают всю бизнес-логику. Критичные проверки дублируются в сервисах.
public class UserService : IUserService
{
public async Task<RegistrationResult> RegisterUserAsync(UserRegistrationDto dto)
{
// Повторная проверка "на всякий случай"
if (dto == null) throw new ArgumentNullException(nameof(dto));
// Сложная бизнес-логика
if (await _repository.IsEmailBannedAsync(dto.Email))
{
return RegistrationResult.Failed("Email находится в чёрном списке.");
}
// ...
}
}
Важно: Всегда возвращаю клиенту понятные сообщения об ошибках (желательно на его языке) в структурированном формате (например, JSON с полем errors), но никогда не раскрываю внутренние детали реализации или стек-трейсы в продакшене.