如何解决如何强制MVC验证IValidatableObject
| 看来,当MVC验证其首先通过DataAnnotation属性(如必需或范围)运行的模型时,如果其中任何一个失败,它将跳过在我的IValidatableObject模型上运行Validate方法。 有没有办法让MVC继续运行该方法,即使其他验证失败呢?解决方法
您可以通过传入ValidationContext的新实例来手动调用Validate(),如下所示:
[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()方法中将该属性设置为true,然后在控制器中手动调用该方法之前进行检查。
因此,在您的控制器中:
if (!ModelState.IsValid) {
if (!model.Validated) {
var validationResults = model.Validate(new ValidationContext(model,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<ValidationResult> Validate(ValidationContext validationContext) {
// perform validation
Validated = true;
}
, 有一种方法可以做到,而无需在每个控制器操作的顶部都添加样板代码。
您需要用自己的一个替换默认的模型联编程序:
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));
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<T>(this IEnumerable<T> source,Func<T,bool> 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;
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。