You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 08:52:53