似乎当MVC验证一个模型时,它首先通过DataAnnotation属性(如required或range)运行,如果其中任何一个失败,它就会跳过在我的IValidatableObject模型上运行validates方法。
有没有办法让MVC在其他验证失败的情况下继续运行该方法?
发布于 2011-06-22 05:07:42
您可以通过传入一个新的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, 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;
}发布于 2011-09-23 11:39:48
有一种方法可以做到这一点,而不需要在每个控制器操作的顶部编写样板代码。
您需要将默认模型绑定器替换为您自己的模型绑定器:
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<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;
}https://stackoverflow.com/questions/6431478
复制相似问题