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

ValidationAttribute注入服务实现非侵入式客户端验证方案问询

解决自定义ValidationAttribute客户端验证的服务依赖问题

你遇到的这个问题很典型——ASP.NET的ValidationAttribute特性本身是编译时构造的,没法直接依赖注入服务,而客户端验证需要把服务器端的无效ID集合传递到前端,所以关键是在AttributeAdapter渲染客户端验证属性的时机(服务器渲染页面时)获取服务并拿到数据。

下面给你两种可行的思路,优先推荐第一种通用方案:

方案一:在AttributeAdapter中通过HttpContext获取服务

在AddValidation方法里,你可以从ClientModelValidationContext的ViewContext.HttpContext.RequestServices拿到DI容器中的服务,这样就能获取到IProductService并拿到无效ID集合,再通过MergeAttribute把数据添加到客户端的HTML属性中。

修改你的AttributeAdapter代码:

public class InvalidIdAttributeAdapter : AttributeAdapterBase<InvalidIdAttribute>
{
    private readonly InvalidIdAttribute _attribute;

    public InvalidIdAttributeAdapter(InvalidIdAttribute attribute, IStringLocalizer stringLocalizer) 
        : base(attribute, stringLocalizer)
    {
        _attribute = attribute;
    }

    public override void AddValidation(ClientModelValidationContext context)
    {
        // 从HttpContext的RequestServices获取服务
        var productService = context.ViewContext.HttpContext.RequestServices.GetService<IProductService>();
        if (productService == null)
        {
            throw new InvalidOperationException("请确保IProductService已注册到DI容器中");
        }

        // 获取无效ID集合并格式化为前端可读取的JSON字符串
        var invalidIds = productService.GetIds()
                                       .Where(p => p.Invalid)
                                       .Select(p => p.Id);
        var invalidIdsJson = System.Text.Json.JsonSerializer.Serialize(invalidIds);

        // 添加客户端验证所需的data-val属性
        MergeAttribute(context.Attributes, "data-val", "true");
        MergeAttribute(context.Attributes, "data-val-invalidid", GetErrorMessage(context));
        MergeAttribute(context.Attributes, "data-val-invalidid-invalidids", invalidIdsJson);
    }

    public override string GetErrorMessage(ModelValidationContextBase validationContext)
    {
        return _attribute.FormatErrorMessage(validationContext.ModelMetadata.GetDisplayName());
    }
}

注册适配器提供器

要让ASP.NET识别你的适配器,需要注册IValidationAttributeAdapterProvider,在Program.cs(或Startup.cs)中添加:

builder.Services.AddSingleton<IValidationAttributeAdapterProvider, InvalidIdAttributeAdapterProvider>();

// 适配器提供器实现
public class InvalidIdAttributeAdapterProvider : IValidationAttributeAdapterProvider
{
    private readonly IValidationAttributeAdapterProvider _innerProvider;

    public InvalidIdAttributeAdapterProvider(IValidationAttributeAdapterProvider innerProvider)
    {
        _innerProvider = innerProvider;
    }

    public IAttributeAdapter GetAdapter(ValidationAttribute attribute, IStringLocalizer stringLocalizer)
    {
        if (attribute is InvalidIdAttribute invalidIdAttr)
        {
            return new InvalidIdAttributeAdapter(invalidIdAttr, stringLocalizer);
        }
        return _innerProvider.GetAdapter(attribute, stringLocalizer);
    }
}

编写客户端验证逻辑

最后需要添加jQuery Validate的自定义验证方法,读取我们传递的无效ID集合:

// 注册自定义验证规则
$.validator.addMethod("invalidid", function (value, element, params) {
    if (!value) return true; // 允许空值,根据你的业务需求调整
    const invalidIds = JSON.parse(params);
    return !invalidIds.includes(parseInt(value, 10));
});

// 绑定不显眼验证的适配器
$.validator.unobtrusive.adapters.add("invalidid", ["invalidids"], function (options) {
    options.rules["invalidid"] = options.params.invalidids;
    options.messages["invalidid"] = options.message;
});

方案二:提前在控制器/页面中传递数据(备选)

如果不想在Adapter中直接获取服务,你可以在使用该验证特性的控制器或PageModel中,提前把无效ID集合放到ViewData或ModelMetadata.AdditionalValues中,然后在Adapter里读取:

控制器中设置数据:

public IActionResult Edit(int id)
{
    var productService = HttpContext.RequestServices.GetService<IProductService>();
    var invalidIds = productService.GetIds().Where(p => p.Invalid).Select(p => p.Id);
    ViewData["InvalidProductIds"] = System.Text.Json.JsonSerializer.Serialize(invalidIds);
    return View();
}

Adapter中读取数据:

public override void AddValidation(ClientModelValidationContext context)
{
    var invalidIdsJson = context.ViewContext.ViewData["InvalidProductIds"] as string;
    if (string.IsNullOrEmpty(invalidIdsJson))
    {
        // 处理无数据的情况,比如跳过客户端验证
        return;
    }

    // 后续添加data-val属性逻辑和方案一一致
}

这种方式的缺点是需要在每个使用该特性的页面手动设置数据,通用性不如方案一。

注意事项

  1. 服务注册:确保IProductService已经正确注册到DI容器(比如builder.Services.AddScoped<IProductService, ProductService>())。
  2. 数据缓存:如果GetIds()的数据不经常更新,建议在IProductService中缓存结果,避免每次渲染页面都查询数据库/数据源,提升性能。
  3. 数据格式:用JSON传递ID集合比用分隔符更可靠,能避免ID包含特殊字符导致的解析错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 06:46:57