如何将IValidatableObject与Razor语法集成实现运行时验证?
问题描述
使用验证特性装饰视图模型属性时,输入无效时向用户显示错误消息非常简单。比如下面的代码就能生成对应的错误提示:
[Required] [EmailAddress] public string Email { get; set; }
<span asp-validation-for="Input.Email" class="text-danger"></span>

但现在遇到一个问题:需要验证的最小值和最大值只有在运行时才能确定,没法直接给Range特性传参数:
[Range(<some-unknown-value>, <another-unknown-value>)] public double MyValue { get; set; }
所以打算通过实现IValidatableObject接口来自定义验证规则,相关文档说这个接口能实现类似效果,但又有文档指出这种验证方式更适合REST调用,而非.NET Core MVC的设计模式。想知道有没有人成功用自定义验证的模式完成这类功能。
解决方案
当然可以在.NET Core MVC里用IValidatableObject实现运行时动态范围验证,而且完全适配MVC的表单验证流程,下面是具体实现步骤:
创建视图模型并实现IValidatableObject接口
可以通过构造函数注入依赖(比如配置、服务)来获取运行时的范围值,在Validate方法里编写自定义验证逻辑:public class MyViewModel : IValidatableObject { // 需要验证的属性 public double MyValue { get; set; } // 存储运行时获取的范围参数 private readonly double _minValue; private readonly double _maxValue; // 从配置注入动态范围值 public MyViewModel(IConfiguration config) { _minValue = config.GetValue<double>("Validation:MinValue"); _maxValue = config.GetValue<double>("Validation:MaxValue"); } // 实现自定义验证逻辑 public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { if (MyValue < _minValue || MyValue > _maxValue) { yield return new ValidationResult( $"值必须在{_minValue}到{_maxValue}之间", new[] { nameof(MyValue) } ); } } }在视图中显示验证错误
和使用特性验证的方式完全一致,用asp-validation-for标签助手就能自动绑定并显示错误:<div class="form-group"> <label asp-for="MyValue"></label> <input asp-for="MyValue" class="form-control" /> <span asp-validation-for="MyValue" class="text-danger"></span> </div>在控制器中触发验证
MVC的模型绑定流程会自动触发IValidatableObject的验证逻辑,只需在控制器里检查ModelState.IsValid即可:[HttpPost] public IActionResult Submit(MyViewModel model) { if (!ModelState.IsValid) { return View(model); } // 执行业务逻辑 return RedirectToAction("Success"); }
补充说明
- 这种方式默认是服务端验证,如果需要客户端验证,可以额外编写jQuery自定义验证规则,或者结合远程验证实现。
- 所谓"更适合REST"的说法只是因为REST API依赖模型验证返回结构化错误,但MVC同样可以完美适配该验证方式,二者的模型验证逻辑本质是通用的,仅错误展示形式不同。
内容的提问来源于stack exchange,提问作者Duncan
相关产品推荐
相关产品推荐

