asp.net-core - 如何在 ASP.NET Core 中创建一个也会触发无效输入的自定义验证器?
问题描述
我为DateTime
ASP.NET Core 3.1 中的字段创建了一个自定义验证器,如下所示:
[CustomDate]
public DateTime DOB { get; set; }
public class CustomDate : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
//… some logic
}
}
但是,我的问题是这个自定义验证器只有在我将日期值放入文本框控件时才会触发。它不会因无效输入而触发,例如当我在文本框中输入字符串“aaa”时。
我的问题是如何使这个自定义验证器即使对于“字符串”等无效输入也能触发。
原因是我想让这个自定义验证器替换[Required]
,[ReqularExpression]
等。一种“一个环(验证器)来统治它们”。我怎样才能做到这一点?
解决方案
TL;DR:当您提交无法转换为 的值时DateTime
,模型绑定失败。由于已经存在与该属性关联的验证错误,因此后续验证(包括您的CustomDate
验证器)不会触发。但是,您的属性仍在验证中:如果您输入 的值aaa
,ModelState.IsValid
将返回false
。
您最初发布的代码应该可以正常工作 - 但我怀疑它没有按照您期望的方式工作。最值得注意的是,您的困惑可能源于以下陈述:
“……这个自定义验证器仅在我将日期值放入文本框控件时触发。”
这也是真的!让我来看看整个过程。
原始代码
为了帮助说明这一点,我希望您不要介意我恢复您的原始代码示例,因为对工作进行具体参考很有用。
[CustomDate]
public DateTime DOB { get; set; }
public class CustomDate : Attribute, IModelValidator
{
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
{
if (Convert.ToDateTime(context.Model) > DateTime.Now)
return new List<ModelValidationResult> {
new ModelValidationResult("", "Invalid - future date")
};
else if (Convert.ToDateTime(context.Model) < new DateTime(1970, 1, 1))
return new List<ModelValidationResult> {
new ModelValidationResult("", "Invalid - date is less than 1970 year")
};
else
return Enumerable.Empty<ModelValidationResult>();
}
}
验证过程
在我完成这个过程之前,这里有四个重要的基本考虑因素需要注意:
- 模型绑定发生在模型验证之前。
- 如果绑定失败,模型绑定将引发自己的验证错误。
- 验证属性仅对剩余的属性进行评估
IsValid
。 - 该
ModelValidationContext.Model
属性被键入到已验证的属性中——因此,在这种情况下,是一个DateTime
值。
用例 #1:无效值
aaa
考虑到这些考虑,当您在映射到已验证DOB
属性的字段中提交 eg 值时,会发生以下情况:
- 模型绑定器尝试将 的值绑定
aaa
到DateTime
属性。 - 模型绑定器失败,将 a 添加
ModelError
到您的ModelStateDictionary
. - 您的
CustomDate
验证器永远不会触发,因为该字段已经失败验证。
用例 #2:缺失值
查看另一个测试用例很有启发性。与其放入aaa
,不如根本不放入值。在这种情况下,该过程看起来有点不同:
DateTime
模型绑定器没有为您的属性找到值,因此不会发生绑定。- 您的模型的属性初始化为
DateTime
的默认值0001-01-01 00:00:00
。 - 您的
CustomDate
验证器触发,添加一个ModelError
因为“无效 - 日期小于 1970 年”。
分析
正如您在上面看到的,当提交虚假日期时,您的验证器确实没有触发。CustomDate
但这并不意味着验证不会发生。相反,验证已经发生并且失败了。如果您输入一个有效的日期——或者根本不输入一个日期——那么模型绑定错误将不会发生,并且您的CustomDate
验证器将按预期执行。
重新审视你的问题
“如何使这个自定义验证器即使对于'字符串'等无效输入也能触发。”
最终,我没有回答这个问题。但我认为我的回答将解释为什么会发生这种情况,以及为什么您的输入仍然得到验证。请记住,即使您的CustomDate
验证器确实触发了,它的行为也会与您根本没有提交值一样,因为该context.Model
值会默认为0001-01-01 00:00:00
. 主要区别在于您没有收到相同的错误消息,因为错误来自不同的来源。
强制验证
我不推荐这样做,但如果您真的希望CustomDate
验证器触发,您可以将其应用于string
属性。在这种情况下,模型绑定不会失败,您的CustomDate
验证器仍然会被调用。如果您追求这一点,则需要进行额外的验证以确保日期格式正确(例如,通过抢占或处理InvalidFormatException
s)。但是,当然,您的日期将存储为string
,这可能不是您想要的。
代码建议
这有点超出您原始问题的范围,但是当我在这里时,我建议您这样做:
- 你不需要
Convert.ToDateTime()
在你的验证器中做 a;您的context.Model
字段已经是DateTime
. 您只需要将它转换回一个DateTime
对象(例如,(DateTime)context.Model
),以便您的代码知道这一点。 - 至少,您应该考虑使用
<input type="date" />
( reference ),在大多数浏览器上,它会将输入限制为正确的日期,同时还提供基本的日期选择器。 - 或者,如果您需要对演示文稿和客户端验证进行更多控制,您可以考虑实施许多用 JavaScript 编写的更复杂的日期/时间控件。
推荐阅读
- vue.js - vue i18n中的复数
- php - PHP:复制文件,同时写入
- xamarin - 如何在我的 Xamarin.Forms 应用程序中实现相机?
- sql-server - 机器学习服务器 (SQL):如何备份和恢复 Python 和包
- javascript - 重置事件列表器?
- python - Python,匹配正则表达式
- html - 如何在响应式问题中将 div 元素对齐到图像上?
- java - Junit 如何模拟 namedParameterJdbcTemplate.query(" ", parameters,(ResultSet rs))
- json - 在具有 maven 依赖项的列表中放心的 JSONPath 表达式
- airflow - 你有两个airflow.cfg 文件