Mr *_*ell 32 validation asp.net-mvc-3
似乎当MVC验证模型时,它首先运行DataAnnotation属性(如required或range),如果其中任何一个失败,它会跳过在我的IValidatableObject模型上运行Validate方法.
有没有办法让MVC继续运行该方法,即使其他验证失败了?
gra*_*ram 35
您可以通过传入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);
}
}
Run Code Online (Sandbox Code Playgroud)
这种方法的一个警告是,在没有属性级别(DataAnnotation)错误的情况下,验证将运行两次.为避免这种情况,您可以向模型添加属性,例如布尔验证,在运行后在Validate()方法中设置为true,然后在手动调用控制器中的方法之前进行检查.
所以在你的控制器中:
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);
}
Run Code Online (Sandbox Code Playgroud)
在你的模型中:
public bool Validated { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
// perform validation
Validated = true;
}
Run Code Online (Sandbox Code Playgroud)
ugl*_*ger 26
有一种方法可以在不需要每个控制器操作顶部的样板代码的情况下完成.
您需要使用自己的一个替换默认模型绑定器:
protected void Application_Start()
{
// ...
ModelBinderProviders.BinderProviders.Clear();
ModelBinderProviders.BinderProviders.Add(new CustomModelBinderProvider());
// ...
}
Run Code Online (Sandbox Code Playgroud)
您的模型绑定器提供程序如下所示:
public class CustomModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(Type modelType)
{
return new CustomModelBinder();
}
}
Run Code Online (Sandbox Code Playgroud)
现在创建一个实际强制验证的自定义模型绑定器.这是繁重的工作:
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);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
你也需要一个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;
}
Run Code Online (Sandbox Code Playgroud)