ASP.. NET MVC在绑定/强制转换失败时不验证视图模型



. NET MVC,我有一个模型类实现IValidatableObject,并有一个属性int? Day {get; set;}

如果用户试图为此提交一个字符串,ModelState被正确地标记为无效,但Validate方法中验证的其余部分不运行,因此用户只看到这一个错误,而不是所有内容。

我需要所有的错误一次可用(这是一个真正令人沮丧的用户体验,一次得到一个错误)。所以我希望要么能够覆盖这种行为,并设置Daynull或希望ASP。. NET MVC有一些内置的属性或一些东西来为我做这件事(我自己的验证会把Day当作null,并要求用户纠正这一点)。

我的视图模型:

public class FormViewModel : IValidatableObject
{
public int? Day { get; set; }
public IEnumerable<string> ErrorMessageOrdering { get; } = new List<string>()
{
nameof(Date),
};
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// This method doesn't get run when the user inputs "s" for Day. This is NOT the desired behaviour as other validations in this method wont run
if (!Day.HasValue) 
{
yield return new ValidationResult("Date must include a day", new[] {nameof(Day)});
}
// ... Validate other fields
}
}

我的控制器:

[ServiceFilter(typeof(ConfigSettingsAttribute))]
public class HomeController : Controller
[Route("/form")]
[HttpPost]
public async Task<IActionResult> FormAsync(FormViewModel model)
{
if (!ModelState.IsValid)
{
return View(model)
}
}
}

我的观点:

@model FormViewModel
@if (!ViewData.ModelState.IsValid)
{
<partial name="_ErrorSummary" model=@(Model.ErrorMessageOrdering) />
}
<form method="post">
<input asp-for="Day" type="text" pattern="[0-9]*" inputmode="numeric" maxlength="2">
@* ... Other fields @*
</form>

当用户输入" "对于Day输入(数字html值似乎对限制用户输入没有影响),视图模型上的Validate方法被跳过,ModelState被填充,Day是带有消息The value 's' is invalid for Day.的无效字段。

当用户没有在Day输入中输入任何东西时,Validate方法确实运行,并且ModelState填充Day无效,消息是我在Validate中设置的。

这里的问题是,因为当用户输入字符串时,没有在视图模型上调用Validate(可能是因为它没有强制转换/绑定值),任何进一步的错误都不会被添加到ModelState中。我更喜欢如果框架设置Day为空,如果它不能绑定而不是短路。如果我能重写这个行为,那就太好了。

请注意,我的问题似乎是,Validate不调用,如果有绑定/转换错误。与Validate方法没有正确编写相反。当绑定错误没有发生时,我确实得到了一个验证失败列表

这是因为这样写!看看你的代码:

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// This method doesn't get run when the user inputs "s" for Day. This is NOT the desired behaviour as other validations in this method wont run
if (!Day.HasValue) 
{
return yield new ValidationResult("Date must include a day", new[] {nameof(Day)});
}
// ... Validate other fields
}

当你的第一次检查返回false-你返回错误(使用return yield new ValidationResult(....)) -所以方法执行停止在这里。

如果您想避免这种情况-您可以在内部构建List<ValidationResult>,并将检测到的任何错误添加到该列表中-然后返回一次最后-包含所有检测到的错误。

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
List<ValidationResult> results = new List<ValidationResult>();
// This method doesn't get run when the user inputs "s" for Day. This is NOT the desired behaviour as other validations in this method wont run
if (!Day.HasValue) 
{
results.Add(new ValidationResult("Date must include a day", new[] {nameof(Day)}));
}
// ... Validate other fields
if (someOtherCondition)
{
results.Add(new ValidationResult("Some other condition was true - returning another error", new[] {nameof(Day)}));
}
// and more - as needed
..
// in the end - return the list
return results;
}

当然,这种方法只有在你之前检测到的错误不会使你无法继续下去的情况下才有效——例如,如果你的一些数据是null,你可能无法做任何进一步的检查。

但基本上-而不是返回每个单独的错误,这允许您验证多个方面,并返回一次,带有验证结果列表现在你的UI需要能够处理获取多个验证结果,并显示它们!

最新更新