Как заставить MVC проверять объект IValidatableObject
Похоже, что когда MVC проверяет модель, которая сначала запускается через атрибуты DataAnnotation (например, требуемый или диапазон), и если кто-либо из них не работает, он пропускает метод Validate в моей модели IValidatableObject.
Есть ли способ запустить MVC и запустить этот метод, даже если другая проверка не удалась?
- Проверка нокаута
- Целочисленная проверка для ввода
- Проверка даты с помощью проверки подлинности ASP.NET
- Инструмент проверки XML-схемы (XSD)?
- Использовать проверку ASP.NET MVC с помощью jquery ajax?
- Как работает SQL-инъекция из комикса XBCD Bobby Tables?
- Как заставить проверку формы html5 без отправки ее через jQuery
- Как установить атрибут maxlength на h: inputTextarea
- Angularjs препятствуют отправке формы при неудачной проверке ввода
- Использование правил проверки WPF и отключение кнопки «Сохранить»
- Проверка верстки ViewModel для списка
- Аутентификация со старым паролем больше не поддерживается, используйте пароли типа 4.1
- Убедитесь, что вход в UITextField только числовой
Вы можете вручную вызвать Validate (), передав новый экземпляр ValidationContext, например:
[HttpPost] public ActionResult Create(Model model) { if (!ModelState.IsValid) { var errors = model.Validate(new ValidationContext(model, null, null)); foreach (var error in errors) foreach (var memberName in error.MemberNames) ModelState.AddModelError(memberName, error.ErrorMessage); return View(post); } }
Предостережение этого подхода заключается в том, что в тех случаях, когда ошибки на уровне свойств (DataAnnotation) отсутствуют, проверка будет выполняться дважды. Чтобы этого избежать, вы можете добавить свойство в свою модель, скажем, логическое значение Validated, которое вы установили в метод Validate (), когда оно выполняется, а затем проверите перед тем, как вручную вызвать метод в вашем controllerе.
Итак, в вашем controllerе:
if (!ModelState.IsValid) { if (!model.Validated) { var validationResults = model.Validate(new ValidationContext(model, null, null)); foreach (var error in validationResults) foreach (var memberName in error.MemberNames) ModelState.AddModelError(memberName, error.ErrorMessage); } return View(post); }
И в вашей модели:
public bool Validated { get; set; } public IEnumerable Validate(ValidationContext validationContext) { // perform validation Validated = true; }
Есть способ сделать это, не требуя кода шаблона в верхней части каждого действия controllerа.
Вам нужно будет заменить стандартное связующее устройство на свой собственный:
protected void Application_Start() { // ... ModelBinderProviders.BinderProviders.Clear(); ModelBinderProviders.BinderProviders.Add(new CustomModelBinderProvider()); // ... }
Ваш поставщик связующего устройства выглядит следующим образом:
public class CustomModelBinderProvider : IModelBinderProvider { public IModelBinder GetBinder(Type modelType) { return new CustomModelBinder(); } }
Теперь создайте настраиваемое связующее устройство, которое фактически заставляет валидацию. Здесь тяжелый подъем:
public class CustomModelBinder : DefaultModelBinder { protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext) { base.OnModelUpdated(controllerContext, bindingContext); ForceModelValidation(bindingContext); } private static void ForceModelValidation(ModelBindingContext bindingContext) { var model = bindingContext.Model as IValidatableObject; if (model == null) return; var modelState = bindingContext.ModelState; var errors = model.Validate(new ValidationContext(model, null, null)); foreach (var error in errors) { foreach (var memberName in error.MemberNames) { // Only add errors that haven't already been added. // (This can happen if the model's Validate(...) method is called more than once, which will happen when // there are no property-level validation failures.) var memberNameClone = memberName; var idx = modelState.Keys.IndexOf(k => k == memberNameClone); if (idx < 0) continue; if (modelState.Values.ToArray()[idx].Errors.Any()) continue; modelState.AddModelError(memberName, error.ErrorMessage); } } } }
Вам также понадобится метод расширения IndexOf. Это дешевая реализация, но она будет работать:
public static int IndexOf(this IEnumerable source, Func predicate) { if (source == null) throw new ArgumentNullException("source"); if (predicate == null) throw new ArgumentNullException("predicate"); var i = 0; foreach (var item in source) { if (predicate(item)) return i; i++; } return -1; }