.NET 6 MVC自定义RequiredIf条件验证属性不生效问题
问题根因
你的验证不生效是3个核心问题叠加导致的:
- POST方法逻辑错误:Create提交接口里
ModelState.IsValid校验失败时直接return null,框架会返回204无内容响应,不会回到表单视图,也不会输出验证错误,自然看不到触发效果。而且外层套的try-catch完全无意义,模型验证流程不会抛异常,catch分支永远走不到。 - 自定义验证特性默认不校验空值:直接继承
ValidationAttribute的自定义特性,默认不会对null、空字符串类型的输入执行IsValid逻辑——只有内置的RequiredAttribute默认会对空值做校验。另外你原来写的反射取属性名的逻辑完全冗余:传入的PropertyName本身就是当前属性的名称,反射取到的Name属性和它永远相等,等于做了无用功,一旦传入的属性名不存在还会直接抛空引用异常。 - .NET 6可空类型隐式验证冲突:.NET 6项目模板默认启用可空引用类型,你模型里的
FirstName、LastName声明为非可空string,框架模型绑定阶段会自动给这些属性加内置的Required验证,优先于你的自定义验证执行,逻辑会被打乱。
修复步骤
1. 修正控制器提交逻辑
把Create的POST方法改成验证失败时返回原视图,传入模型才能正常展示错误:
[HttpPost] [ValidateAntiForgeryToken] public ActionResult Create(Person personModel) { if(ModelState.IsValid) { return RedirectToAction(nameof(Index)); } // 验证失败返回表单视图,传入当前模型 return View(personModel); }
其他Edit、Delete动作的逻辑如果后续要加验证,也要遵循同样的规则,不要在验证失败时返回null。
2. 重写自定义验证特性
让特性支持空值校验,去掉冗余的反射逻辑,修复后的代码如下:
[AttributeUsage(AttributeTargets.Property)] public class RequiredIfAttribute : ValidationAttribute, IClientModelValidator { private readonly string _propertyName; private readonly bool _desiredConfigValue; public RequiredIfAttribute(string propertyName, bool desiredConfigValue) { _propertyName = propertyName; _desiredConfigValue = desiredConfigValue; } // 标记特性需要验证上下文,强制框架对null/空值也执行校验 public override bool RequiresValidationContext => true; protected override ValidationResult? IsValid(object? value, ValidationContext validationContext) { // 直接读静态配置判断当前字段是否需要必填 var fieldConfig = RequiredFieldsData.PropertiesPair(); bool needValidate = fieldConfig.TryGetValue(_propertyName, out bool configValue) && configValue == _desiredConfigValue; // 不需要必填的场景直接放行 if (!needValidate) { return ValidationResult.Success; } // 需要必填时校验值是否为空 if (string.IsNullOrWhiteSpace(value?.ToString())) { return new ValidationResult(ErrorMessage); } return ValidationResult.Success; } public void AddValidation(ClientModelValidationContext context) { MergeAttribute(context.Attributes, "data-val", "true"); var errorMessage = FormatErrorMessage(context.ModelMetadata.GetDisplayName()); MergeAttribute(context.Attributes, "data-val-requiredif", errorMessage); // 把验证参数传给前端,供客户端验证使用 MergeAttribute(context.Attributes, "data-val-requiredif-property", _propertyName); MergeAttribute(context.Attributes, "data-val-requiredif-value", _desiredConfigValue.ToString().ToLower()); } private bool MergeAttribute(IDictionary<string, string> attributes, string key, string value) { if (attributes.ContainsKey(key)) { return false; } attributes.Add(key, value); return true; } }
3. 关闭隐式必填验证冲突
在Program.cs里注册MVC服务的代码中,关闭非可空引用类型自动加Required特性的默认行为,避免框架内置验证干扰自定义逻辑:
builder.Services.AddControllersWithViews(options => { options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true; });
如果你不想全局关闭这个配置,也可以把Person模型里的两个字符串属性改成可空类型:
public class Person { public int Id { get; set; } [RequiredIf("FirstName", true, ErrorMessage = "First Name is required")] public string? FirstName { get; set; } [RequiredIf("LastName", true, ErrorMessage ="Last name is required")] public string? LastName { get; set; } }
(可选)补全客户端验证
你现在只加了客户端验证的标记属性,没有对应的JS逻辑,前端无刷新验证不会生效。如果需要客户端即时校验,可以在站点公共JS文件里加如下适配代码:
$.validator.addMethod("requiredif", function (value, element) { // 如果需要做前端动态配置判断可以在这里扩展逻辑,简单场景直接后端校验也可以满足需求 return value.trim().length > 0; }); $.validator.unobtrusive.adapters.addBool("requiredif");
改完之后重新测试就能符合预期:FirstName配置为true时空值提交会触发错误,LastName配置为false时空值可以正常提交。
内容的提问来源于stack exchange,提问作者Shaksham Singh
相关产品推荐
相关产品推荐

