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

为集合属性添加自定义验证:客户端验证失效问题排查

问题分析与解决方案

核心问题

你的AddValidation方法未触发,主要是两个关键原因:

  • 集合属性访问权限不足:Affiliate类中的Campaigns属性没有显式声明为public,默认是私有访问级别。ASP.NET MVC无法扫描私有属性上的验证特性,因此不会触发IClientModelValidator接口的AddValidation方法。
  • 缺少客户端验证规则:即使服务端验证正常,针对集合子项IsSelected的客户端验证,需要额外编写jQuery非侵入式验证规则,才能让前端识别自定义的验证属性。

分步修复

1. 修正集合属性的访问级别

将Affiliate中的Campaigns属性改为public,同时修正类型笔误(原代码中List<Campaigns>应为List<CampaignType>):

public class Affiliate
{
    [Required]
    public int Id { get; set; }
    
    [RequiredAtLeasOneFromCollectionAttribute(ErrorMessage = "请至少选择一个选项")]
    public List<CampaignType> Campaigns { get; set; }

    // 构造函数填充Campaigns逻辑
}

2. 完善自定义验证特性

调整验证逻辑,确保反射获取IsSelected属性的逻辑更健壮,同时保证AddValidation能正确添加客户端属性:

public class RequiredAtLeasOneFromCollectionAttribute : ValidationAttribute, IClientModelValidator
{
    public RequiredAtLeasOneFromCollectionAttribute()
    {
        ErrorMessage = "Please select at least one";
    }

    protected override ValidationResult IsValid(object value, ValidationContext context)
    {
        var itemList = value as IEnumerable<object>;
        if (itemList == null)
            return new ValidationResult(FormatErrorMessage(context.DisplayName));

        foreach (var item in itemList)
        {
            var isSelectedProp = item.GetType().GetProperty("IsSelected");
            if (isSelectedProp != null && (bool)isSelectedProp.GetValue(item))
                return ValidationResult.Success;
        }

        return new ValidationResult(FormatErrorMessage(context.DisplayName));
    }

    public void AddValidation(ClientModelValidationContext context)
    {
        if (context == null)
            throw new ArgumentNullException(nameof(context));
        
        MergeAttribute(context.Attributes, "data-val", "true");
        var errorMsg = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
        MergeAttribute(context.Attributes, "data-val-requiredonefromcollection", errorMsg);
    }

    private bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
    {
        if (attributes.ContainsKey(key))
            return false;
        
        attributes.Add(key, value);
        return true;
    }
}

3. 添加客户端验证脚本

在CSHTML页面的Scripts区块中,添加自定义的jQuery非侵入式验证规则,让前端能识别我们的自定义验证属性:

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
    <script>
        // 自定义验证方法
        $.validator.addMethod("requiredonefromcollection", function (value, element, params) {
            // 匹配所有集合中的IsSelected复选框
            var checkboxes = $('input[name^="Affiliate.Campaigns"][name$=".IsSelected"]');
            return checkboxes.is(':checked');
        });

        // 适配非侵入式验证
        $.validator.unobtrusive.adapters.add("requiredonefromcollection", [], function (options) {
            options.rules["requiredonefromcollection"] = true;
            options.messages["requiredonefromcollection"] = options.message;
        });
    </script>
}

4. 确认验证消息元素

你的CSHTML中已经包含了正确的验证消息绑定元素,无需修改:

<span asp-validation-for="Affiliate.Campaigns" class="text-danger"></span>

验证效果

修复后:

  • AddValidation方法会正常触发,页面源码中会出现data-val和data-val-requiredonefromcollection属性
  • 当所有复选框未选中时,提交表单会先触发客户端验证,显示错误消息
  • 服务端验证依然保持原有生效逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 11:45:00