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

ASP.NET Core运行时修改验证定义:自定义ClientValidatorCache实现问询

Runtime Dynamic Validation Modification in ASP.NET Core (Bypassing ClientValidatorCache)

针对你遇到的ASP.NET Core运行时动态修改验证规则(比如动态标记属性为必填)、且客户端ClientValidatorCache缓存导致变更不生效的问题,我整理了几个更简洁的解决方案,帮你替代当前繁琐的实现思路:

问题背景回顾

你已经通过绕过DefaultModelMetadataProvider的缓存机制,实现了服务端验证规则的动态修改(比如调整ValidationMetadata.IsRequired),但客户端非侵入式验证的data-*属性被ClientValidatorCache缓存,而该类没有可扩展的虚方法或接口,导致动态变更无法同步到前端。当前你采用的重写DefaultValidationHtmlAttributeProvider并自定义CustomClientValidatorCache的方式可行,但实现成本较高。

更优解决方案思路

方案1:直接替换默认的ClientValidatorCache(推荐)

虽然ClientValidatorCache本身没有定义接口,但ASP.NET Core的依赖注入系统允许我们直接替换默认的服务实例。我们可以创建一个不缓存(或按需缓存)的自定义实现,让每次生成客户端验证属性时都重新获取最新的验证器:

  1. 自定义DynamicClientValidatorCache:
public class DynamicClientValidatorCache : ClientValidatorCache
{
    // 重写GetValidators方法,移除缓存逻辑,每次都重新生成验证器
    public override IEnumerable<IClientModelValidator> GetValidators(ClientValidatorCacheKey key, IClientModelValidatorProvider validatorProvider)
    {
        var validators = new List<IClientModelValidator>();
        validatorProvider.CreateValidators(new ClientValidatorProviderContext(key.ModelMetadata, validators));
        return validators;
    }
}
  1. 在Program.cs/Startup.cs中替换默认服务:
builder.Services.Replace(ServiceDescriptor.Singleton<ClientValidatorCache, DynamicClientValidatorCache>());

这个方案只需要两步,就能让客户端验证每次都获取最新的规则,完美匹配你服务端的动态修改逻辑,比重写DefaultValidationHtmlAttributeProvider简洁得多。

方案2:通过ModelMetadata附加值传递动态验证状态

如果不想修改缓存机制,可以通过ModelMetadata的AdditionalValues存储动态验证标记,再自定义客户端验证器读取这个值并生成对应的data-*属性:

  1. 实现自定义客户端验证器:
public class DynamicRequiredClientValidator : IClientModelValidator
{
    public void AddValidation(ClientModelValidationContext context)
    {
        if (context.ModelMetadata.AdditionalValues.TryGetValue("IsDynamicallyRequired", out var value) 
            && value is bool isRequired && isRequired)
        {
            context.Attributes["data-val"] = "true";
            context.Attributes["data-val-required"] = "This field is required.";
        }
    }

    public bool IsReusable => false;
}
  1. 注册验证器提供者:
builder.Services.AddSingleton<IClientModelValidatorProvider, DynamicRequiredClientValidatorProvider>();

public class DynamicRequiredClientValidatorProvider : IClientModelValidatorProvider
{
    public void CreateValidators(ClientValidatorProviderContext context)
    {
        context.Validators.Add(new DynamicRequiredClientValidator());
    }
}
  1. 在你的自定义ModelMetadataProvider中设置动态标记:
public class DynamicModelMetadataProvider : DefaultModelMetadataProvider
{
    protected override void CreateValidationMetadata(ValidationMetadataProviderContext context)
    {
        base.CreateValidationMetadata(context);
        // 这里替换成你的运行时动态判断逻辑
        if (context.Key.Name == "YourPropertyName" && /* 动态条件 */)
        {
            context.ValidationMetadata.AdditionalValues["IsDynamicallyRequired"] = true;
        }
    }

    // 保持你之前绕过缓存的实现逻辑
}

这种方式通过附加元数据传递状态,避开了ClientValidatorCache的缓存限制,适合需要复杂动态规则的场景。

方案3:局部视图/请求级别的动态验证(适合特定场景)

如果只是在特定视图或请求中需要动态修改验证规则,可以通过ViewContext.Items传递状态,再自定义ValidationHtmlAttributeProvider读取并修改属性:

  1. 继承DefaultValidationHtmlAttributeProvider:
public class DynamicValidationHtmlAttributeProvider : DefaultValidationHtmlAttributeProvider
{
    public DynamicValidationHtmlAttributeProvider(IModelMetadataProvider metadataProvider, IClientModelValidatorProvider validatorProvider, IOptions<MvcViewOptions> options)
        : base(metadataProvider, validatorProvider, options)
    {
    }

    public override void AddValidationAttributes(ViewContext viewContext, ModelExplorer modelExplorer, IDictionary<string, string> attributes)
    {
        base.AddValidationAttributes(viewContext, modelExplorer, attributes);
        // 从ViewContext.Items读取动态验证标记
        var cacheKey = $"DynamicRequired_{modelExplorer.ModelMetadata.PropertyName}";
        if (viewContext.Items.TryGetValue(cacheKey, out var value) && value is bool isRequired && isRequired)
        {
            attributes["data-val"] = "true";
            attributes["data-val-required"] = "This field is required.";
        }
    }
}
  1. 替换默认服务:
builder.Services.Replace(ServiceDescriptor.Singleton<IValidationHtmlAttributeProvider, DynamicValidationHtmlAttributeProvider>());
  1. 在Action中设置动态状态:
public IActionResult YourAction()
{
    // 动态标记Name属性为必填
    ViewContext.Items["DynamicRequired_Name"] = true;
    return View();
}

这个方案成本最低,适合局部的动态验证需求,不需要全局修改缓存逻辑。

总结

  • 全局动态验证需求:优先选择方案1,替换ClientValidatorCache实现,代码最简洁,直接匹配服务端的动态修改逻辑。
  • 复杂动态规则场景:选择方案2,通过附加元数据传递状态,避开缓存限制。
  • 局部视图/请求需求:选择方案3,成本最低,针对性强。

内容的提问来源于stack exchange,提问作者Cyril Iselin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 14:12:29