- PVSM.RU - https://www.pvsm.ru -
Передо мной стояла задача сделать условную валидацию для свойства модели в зависимости от значения другого свойства. Гугл [1] говорит, что такая задача встречается довольно часто. Поэтому я решил поделиться тем, как я решил ее в своем проекте.
Самый простым вариантом было бы написать атрибут, который через рефлексию проверял бы значение другого свойства и дело в шляпе. Но такое значение не было универсальным, т.к. даже если задавать имя свойства, которое нужно проверить, то все равно тип другого свойства может отличаться от модели к модели.
После некоторого времени проведенного в поиске красивого решения мне попалась библиотека FluentValidation [2]. Прелесть ее в том, что с ее помощью можно легко задавать проверки для модели практически любой сложности, а если возможностей не хватает — можно написать свой валидатор и легко его назначить для нужного свойства. На сайте проекта есть очень хорошая документация [3]. Вот небольшой пример как это работет:
[Validator(typeof(MyModelValidator))]
public class MyModel
{
public string Name { get; set; }
public string Description { get; set; }
public bool IsDescriptionRequired { get; set; }
}
public class MyModelValidator: AbstractValidator<MyModel>
{
public MyModelValidator()
{
RuleFor(m => m.Name).NotEmpty();
RuleFor(m => m.Description).NotEmpty().When(m => m.IsDescriptionRequired);
}
}
Для простых условий типа Заполнено, Минимальная длина, Максимальная длина и т.п. валидация на клиенте реализована из-коробки. Для реализации более сложных проверок (в моем случае условной) нужно реализовать свой валидатор свойства и реализовать интерфейс IClientValidatable, о котором есть довольно много инфы [4].
Основная идея в том, что нужно реализовать метод, который возвращает набор данных для jQuery.validate [5].
Вместо реализации нового класса для каждого случая когда нужно реализовать валидацию на клиенте мне хотелось создать универсальный клиентский валидатор, которому можно передать набор правил. Получилось что-то такое:
using GetClientValidationRulesFunc = Func<ModelMetadata, ControllerContext, IEnumerable<ModelClientValidationRule>>;
class ClientValidator : PropertyValidator, IClientValidatable
{
private readonly GetClientValidationRulesFunc _getClientValidationRulesFunc;
public ClientValidator(GetClientValidationRulesFunc getClientValidationRulesFunc) : base((string)null)
{
_getClientValidationRulesFunc = getClientValidationRulesFunc;
}
protected override bool IsValid(PropertyValidatorContext context)
{
// Suppress any server side validation
return true;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
return _getClientValidationRulesFunc(metadata, context);
}
}
И использовать в его можно примерно так:
public class MyModelValidator: AbstractValidator<MyModel>
{
public MyModelValidator()
{
RuleFor(m => m.Name).NotEmpty();
RuleFor(m => m.Description).NotEmpty().When(m => m.IsDescriptionRequired).SetValidator(new ClientValidator(GetValidationRules));
}
public IEnumerable<ModelClientValidationRule> GetValidationRules(ModelMetadata metadata,
ControllerContext context)
{
yield return new ModelClientValidationRule
{
ErrorMessage = "Description required",
ValidationType = "validateDescription"
};
}
}
Таким образом можно сохранить гибкость библиотеки и отдельно задать правила для клиентской валидации. Еще стоит сказать что для того, чтобы на клиенте валидация тоже выполнялась нужно создать свою функции и адаптер для нее:
$.validator.unobtrusive.adapters.addBool("validateDescription");
$.validator.addMethod("validateDescription", function (value, element, param) {
if ( $("#IsDescriptionRequired").val() === "true" ) {
return $.trim($("#Description").val()).length > 0;
}
return true;
});
Статья получилась немного скомканная и суховата, но очень сложно раскрыть все тонкости в одной статье. Возможно это кому-то будет полезным, а если будет потребность, я могу подробнее рассказать о интересующих деталях.
Загрузить пакет можно с помощью NuGet: FluentValidation.Mvc3 с зависимостью FluentValidation
Автор: firestarter
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/asp-net-mvc/9088
Ссылки в тексте:
[1] Гугл: https://www.google.com/#hl=en&sclient=psy-ab&q=asp.net+mvc+conditional+validation&oq=asp.net+mvc+conditional+validation&aq=f&aqi=&aql=&gs_l=hp.3...0.0.6.401073.0.0.0.0.0.0.0.0..0.0...0.0.BBRZxiy_c7U&pbx=1&bav=on.2,or.r_gc.r_pw.r_cp.r_qf.,cf.osb&fp=50e01a6936966f69&biw=1473&bih=774
[2] FluentValidation: http://fluentvalidation.codeplex.com/
[3] документация: http://fluentvalidation.codeplex.com/documentation
[4] много инфы: https://www.google.com/#hl=en&sclient=psy-ab&q=asp.net+mvc+3+iclientvalidatable&oq=asp.net+iclientvalidatable&aq=2bK&aqi=g-K1g-b1g-bK1&aql=&gs_l=hp.3.2.0i30j0i8j0i8i30.0.0.2.1070.0.0.0.0.0.0.0.0..0.0...0.0.57PtyHYVmqk&pbx=1&bav=on.2,or.r_gc.r_pw.r_cp.r_qf.,cf.osb&fp=50e01a6936966f69&biw=1473&bih=774
[5] jQuery.validate: http://docs.jquery.com/Plugins/Validation
Нажмите здесь для печати.