看來,當MVC驗證它首先通過DataAnnotation屬性(如必需或範圍)運行的模型,並且如果其中任何一個失敗,它將跳過在我的IValidatableObject模型上運行驗證方法。如何強制MVC驗證IValidatableObject
有沒有辦法讓MVC繼續並運行該方法,即使其他驗證失敗?
看來,當MVC驗證它首先通過DataAnnotation屬性(如必需或範圍)運行的模型,並且如果其中任何一個失敗,它將跳過在我的IValidatableObject模型上運行驗證方法。如何強制MVC驗證IValidatableObject
有沒有辦法讓MVC繼續並運行該方法,即使其他驗證失敗?
您可以手動通過傳遞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()方法中設置爲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;
}
感謝它的工作。但我得到了一個問題。是什麼原因cos Ivalidate對象正在射擊。有時它可能不? – user998405
有一個辦法做到這一點,而無需樣板代碼在每個控制器動作的頂部。
你需要用自己的一個替換默認的模型綁定:
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;
}
老實說,我開始喜歡這種默認行爲。如果您在您的Validate方法中進行業務級別的驗證,這涉及像數據庫連接這樣的昂貴的東西,那麼除非模型有效,否則最好不要調用它們。 – Graham