在ASP.NET MVC3中验证表单时,是否有任何方法可以忽略某些属性(在POCO上)?

时间:2011-03-06 07:20:12

标签: c# asp.net-mvc validation asp.net-mvc-3

我有一个注册新用户注册向导。当我尝试转到第2页时,我收到验证错误,因为我的User对象尚未完全填充。有什么办法可以告诉每个ActionMethod在检查ModelState.IsValid检查时忽略某些属性吗?

例如。 (简体,pseduo代码)

public class User
{
   [Required]
   public string Name; // Asked on page 1.
   [Required]
   public int Age; // Asked on page 1.
   [Required]
   public string Avatar;  // Asked on Page 2.
}

它抱怨说阿凡达是必需的/不能为空。但我没有机会要求用户填写此内容,直到下一页。

是否可以在第1页中要求忽略此检查?

7 个答案:

答案 0 :(得分:20)

答案 1 :(得分:5)

史蒂夫桑德森的asp.net mvc 2书,第486页讨论了这一点。

创建一个继承自ActionFilterAttribute的自定义属性ValidateIncomingValuesOnlyAttribute,并将其应用于控制器类。

覆盖OnActionExecuting方法:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{

var modelState = filterContext.Controller.ViewData.ModelState;
var incomingValues = filterContext.Controller.ValueProvider;

var keys = modelState.Keys.Where(x => !incomingValues.ContainsPrefix(x));
foreach(var key in keys)
{
modelState[key].Errors.Clear();
}
}

这样,您只需验证与向导中每个步骤相关的数据。然后,您需要一个没有数据输入的确认页面,以将验证的数据发送到服务器。

但最重要的是,阅读史蒂夫桑德森的书,它为这个和你的另一个问题提供了一个有效的解决方案。

附录:

如果您决定映射到视图模型而不是上述内容,请小心,因为您将不得不:

一个。不使用验证dataannotation属性装饰viewmodel属性,在这种情况下,只有在用户填写整个向导并尝试提交到数据库后才会进行验证。从用户的角度来看,这将非常简单......

湾否则,您仍然必须使用S Sanderson描述的技术,即清除与当前步骤中的字段无关的任何验证错误。

我没有看到接受的答案是回答了问题。

答案 2 :(得分:3)

要忽略ModelState中的属性,这是最简单的代码。

if (ModelState["PropertyName"] != null) ModelState["PropertyName"].Errors.Clear();

答案 3 :(得分:0)

IgnoreModelErrors自定义类怎么样?

http://mrbigglesworth79.blogspot.in/2011/12/partial-validation-with-data.html


继承ActionFilterAttribute类,并清除OnActionExecuting中的错误[基于匹配的名称或正则表达式],如上面的链接所示。这将更清洁。

答案 4 :(得分:0)

与回发数据完全匹配的ViewModel通常是推荐的技术,因为它非常可预测,并且您可以获得强类型,脚手架等的所有好处。另一方面,使用BindAttribute可能需要您使用未重新过帐的属性,并且在更改属性名称但未绑定BindAttribute Include或Exclude字符串时可能导致运行时出现静默失败。避免使用验证属性在MVC中有许多缺点,需要用其他验证技术(如IValidatableObject或FluentValidation)替换。

尽管ViewModel具有所有优点以及BindAttribute附带的警告,但有时仍然可以优先使用BindAttribute并部分发布到模型/视图模型。此ActionFilterAttribute涵盖了这种确切的情况。这需要代码@awrigley进一步引用,但不是基于ValueProvider清除错误,而是根据BindAttribute的使用(例如Include和Exclude)清除错误。此属性可以安全地添加到GlobalFilterCollection中,因为它不会在未应用BindAttribute时更改MVC验证的行为。请注意:我没有大量使用它,但它适用于我的基本情况。

using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web.Mvc;

/// <summary>
/// When the BindAttribute is in use, validation errors only show for values that 
/// are included or not excluded.
/// </summary>
public class ValidateBindableValuesOnlyAttributes : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var modelState = filterContext.Controller.ViewData.ModelState;
        var includedProperties = filterContext.ActionDescriptor.GetParameters()
            .SelectMany(o => o.BindingInfo.Include.Select(name => (string.IsNullOrWhiteSpace(o.BindingInfo.Prefix) ? "" : o.BindingInfo.Prefix + ".") + name));
        var excludedProperties = filterContext.ActionDescriptor.GetParameters()
            .SelectMany(o => o.BindingInfo.Exclude.Select(name => (string.IsNullOrWhiteSpace(o.BindingInfo.Prefix) ? "" : o.BindingInfo.Prefix + ".") + name));

        var ignoreTheseProperties = new List<KeyValuePair<string, ModelState>>();
        if (includedProperties.Any())
        {
            ignoreTheseProperties.AddRange(modelState.Where(k => !includedProperties.Any(name => Regex.IsMatch(k.Key, "^" + Regex.Escape(name) + @"(\.|\[|$)"))));
        }
        ignoreTheseProperties.AddRange(modelState.Where(k => excludedProperties.Any(name => Regex.IsMatch(k.Key, "^" + Regex.Escape(name) + @"(\.|\[|$)"))));

        foreach (var item in ignoreTheseProperties)
        {
            item.Value.Errors.Clear();
        }
    }
}

答案 5 :(得分:0)

我有一个不应该被验证的参考实体。

在操作开始时将其从验证中删除:

[HttpPost]
public async Task<IActionResult> Post([FromBody] Contact contact)
{
  var skipped = ModelState.Keys.Where(key => key.StartsWith(nameof(Contact.Portfolios)));
  foreach (var key in skipped)
    ModelState.Remove(key);
    //ModelState doesn't include anything about Portfolios which we're not concerned with

  if (!ModelState.IsValid)
    return BadRequest(ModelState);

  //Rest of action
}

答案 6 :(得分:0)

public override void OnActionExecuting(ActionExecutingContext context)
{
    var modelstate = context.ModelState;
    var keys = modelstate.Keys.Where(x => ExculdeFeilds.Split(",").ToList().Contains(x));
    foreach (var item in keys)
    {
        modelstate[item].ValidationState = ModelValidationState.Valid;
    }
    if (!modelstate.IsValid)
    {
        context.Result = new BadRequestObjectResult(context.ModelState);
    }
}