C#跨ValidationAttribute共享ValidationContext失败问题咨询
问题原因分析
你遇到的核心问题是:ASP.NET Core 对每个属性级别的 ValidationAttribute 会创建独立的 ValidationContext 实例。也就是说,ValidatorAttributeOne 里存入 validationContext.Items 的数据,完全不会被 ValidatorAttributeTwo 的 ValidationContext 共享,自然就会抛出键不存在的异常。
解决方案
针对你的需求,我推荐两种可行的实现方式,按优先级排序:
方案一:改用模型级验证(推荐)
模型级验证只会使用同一个 ValidationContext,能直接控制验证逻辑的顺序和依赖关系,代码可读性和维护性更好。
你可以让模型实现 IValidatableObject 接口,把两个验证逻辑按顺序整合到 Validate 方法中:
public class CustomeModel : IValidatableObject { public string usernames { get; set; } public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { // 第一步:执行原 ValidatorAttributeOne 的验证逻辑 bool isOneValid = ValidateAttributeOne(usernames); if (!isOneValid) { yield return new ValidationResult("ValidatorAttributeOne 验证失败", new[] { nameof(usernames) }); // 第一个验证失败,直接终止后续验证 yield break; } // 第二步:第一个验证通过后,再执行原 ValidatorAttributeTwo 的逻辑 bool isTwoValid = ValidateAttributeTwo(usernames); if (!isTwoValid) { yield return new ValidationResult("ValidatorAttributeTwo 验证失败", new[] { nameof(usernames) }); } } // 抽离原属性验证逻辑为独立方法 private bool ValidateAttributeOne(string value) { // 替换成你原来 ValidatorAttributeOne 里的自定义验证逻辑 return true; } private bool ValidateAttributeTwo(string value) { // 替换成你原来 ValidatorAttributeTwo 里的自定义验证逻辑 return true; } }
如果想保留 Attribute 的形式,也可以创建一个模型级的验证 Attribute:
[AttributeUsage(AttributeTargets.Class)] public class CombinedValidatorAttribute : ValidationAttribute { protected override ValidationResult IsValid(object value, ValidationContext validationContext) { var model = value as CustomeModel; if (model == null) return ValidationResult.Success; // 先执行第一个验证 bool isOneValid = ValidateAttributeOne(model.usernames); if (!isOneValid) { return new ValidationResult("ValidatorAttributeOne 验证失败", new[] { nameof(CustomeModel.usernames) }); } // 第一个通过后执行第二个验证 bool isTwoValid = ValidateAttributeTwo(model.usernames); if (!isTwoValid) { return new ValidationResult("ValidatorAttributeTwo 验证失败", new[] { nameof(CustomeModel.usernames) }); } return ValidationResult.Success; } private bool ValidateAttributeOne(string value) => true; // 替换为原逻辑 private bool ValidateAttributeTwo(string value) => true; // 替换为原逻辑 } // 在模型上应用该属性 [CombinedValidator] public class CustomeModel { public string usernames { get; set; } }
方案二:利用 HttpContext.Items 共享状态(适合必须保留属性级验证的场景)
同一 HTTP 请求中,HttpContext 是全局共享的,你可以把验证状态存在这里。注意要给键加唯一标识(比如结合属性名),避免同一请求中其他属性的验证冲突。
第一步:注册 HttpContextAccessor
在 Program.cs 或 Startup.cs 中注册服务:
builder.Services.AddHttpContextAccessor();
第二步:修改两个验证 Attribute
public class ValidatorAttributeOne : ValidationAttribute { protected override ValidationResult IsValid(object value, ValidationContext validationContext) { var httpContext = validationContext.GetService<IHttpContextAccessor>()?.HttpContext; if (httpContext == null) { // 非 Web 环境下直接执行验证逻辑 bool isValid = true; // 替换为原逻辑 return isValid ? ValidationResult.Success : new ValidationResult("ValidatorAttributeOne 验证失败"); } // 执行自定义验证 bool isValid = true; // 替换为原逻辑 // 用「属性名+验证器名」作为唯一键,避免冲突 string key = $"{validationContext.MemberName}_{nameof(ValidatorAttributeOne)}"; httpContext.Items[key] = isValid; return isValid ? ValidationResult.Success : new ValidationResult("ValidatorAttributeOne 验证失败"); } } public class ValidatorAttributeTwo : ValidationAttribute { private readonly string _dependedFilter; public ValidatorAttributeTwo(string filter) { _dependedFilter = filter; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { var httpContext = validationContext.GetService<IHttpContextAccessor>()?.HttpContext; if (httpContext == null) { // 无法获取 HttpContext 时,默认执行自身验证逻辑 return ExecuteValidationLogic(value); } // 构造对应的共享键 string key = $"{validationContext.MemberName}_{_dependedFilter}"; if (httpContext.Items.TryGetValue(key, out var isValidObj) && isValidObj is bool isValid && isValid) { // 依赖的验证已通过,直接返回成功 return ValidationResult.Success; } // 依赖验证未通过或不存在,执行自身逻辑 return ExecuteValidationLogic(value); } private ValidationResult ExecuteValidationLogic(object value) { // 替换为原 ValidatorAttributeTwo 的自定义验证逻辑 return ValidationResult.Success; } }
内容的提问来源于stack exchange,提问作者191180rk
相关产品推荐
相关产品推荐

