我有一个注册新用户注册向导。当我尝试转到第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页中要求忽略此检查?
答案 0 :(得分:20)
更好的选择是使用ViewModels。
答案 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);
}
}