【问题标题】:Custom Validation Attribute not validating on Model - WebAPI C# and JSON自定义验证属性未在模型上验证 - WebAPI C# 和 JSON
【发布时间】:2020-11-04 00:08:32
【问题描述】:

我创建了一个模型和一个自定义验证属性,以确保该值大于零。我遇到的问题是自定义属性的构造函数被命中,但 IsValid 覆盖从未被命中。

[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class GreaterThanZeroAttribute : ValidationAttribute
{
    /// <summary>
    /// Ensures that the value is greater than zero.
    /// </summary>
    public GreaterThanZeroAttribute()
        : base("The value {0} must be greater than 0.")
    {
    }

    public override bool IsValid(object value)
    {
        bool isValueLong = long.TryParse(value?.ToString(), out long longValue);

        if (isValueLong && longValue > 0)
            return true;
        else
            return false;
    }
}
public class ApplicationModel
{
    [Required]
    public string Name { get; set; }

    [GreaterThanZero]
    public IEnumerable<string> AssignedUserGroupIDs { get; set; }
}

现在,客户端将在 JSON 中设置 AssignedUserGroupIDs。我想验证该集合中的每个 ID 是否大于零。

这是请求的示例 JSON:

{ 
    "name": "Test Application",
    "assignedUserGroupIDs": [ "1", "-1001" ] 
} 

对于它的价值,我使用 JsonOptions 来使用驼峰式大小写作为属性名称并将字符串转换为枚举。我之所以提到这一点,是因为我不确定 Json 是否会导致任何问题。

public static IServiceCollection AddWebApiServices(this IServiceCollection services)
{
    _ = services ?? throw new ArgumentNullException(nameof(services));

    Setup.AddServices(services);
    services.AddMvcCore(ConfigureDefaults)
        .AddJsonFormatters()
        .AddJsonOptions(options =>
        {
            options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
            options.SerializerSettings.Converters.Add(new StringEnumConverter());
        })
        .AddCors("*")
        .AddControllers();

    return services;
}

这里是控制器:

        [HttpPost]
        [Route("applications")]
        [EnableCors(PolicyName = CorsPolicies.Default)]
        public IActionResult CreateApplication([FromBody] ApplicationModel postModel)
        {
            List<long> assignedUserGroups = new List<long>();

            foreach (string stringUserGroupID in postModel.AssignedUserGroupIDs)
            {
                assignedUserGroups.Add(Convert.ToInt64(stringUserGroupID));
            }
            
            // business logic here...

            return new JsonResult(applicationID);
        }

【问题讨论】:

    标签: c# json asp.net-mvc asp.net-web-api model-view-controller


    【解决方案1】:

    如果要验证集合中的每个 ID 是否大于零,则需要将 value 强制转换为 IEnumerable,然后循环遍历列表中的每个项目:

    /*
     * Changes:
     * 1. Rename to CollectionGreaterThanZero to better reflect this validation is
     *    against a collection.
     * 2. Change AttributeUsage to include Field, as there is a backing field behind
     *    AssignedUserGroupIDs { get; set; }, and when the data comes in, the backing
     *    field gets set. That's when you want to run your validation.
     * 3. Set AllowMultiple=false as there is no point to run this validation multiple
     *    times on the same property or field.
    */
    
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field,  AllowMultiple = false)]
    public class CollectionGreaterThanZeroAttribute : ValidationAttribute
    {
        public override bool IsValid(object value)
        {
            // This will case the value to IEnumerable silently.
            var enumerable = value as IEnumerable;
            if (enumerable != null)
            {
                foreach (var item in enumerable)
                {
                    bool isLong = long.TryParse(item?.ToString(), out long longValue);
                    if (!isLong)
                    {
                        return false;
                    }
                }
    
                // I don't know if you consider an empty collection as a valid 
                // collection or not in your domain.
                return true;
            }
    
            return false;
        }
    }
    

    显示它被击中的屏幕截图:

    【讨论】:

    • 现在,客户端将在 JSON 中设置 AssignedUserGroupID。我想验证该集合中的每个 ID 是否大于零。这是请求的示例 JSON:``` { "name": "Test Application", "assignedUserGroupIDs": [ "1", "-1001" ] } ```
    • 我真的很喜欢这种方法。谢谢你。我添加了这个,但是当我在方法的第一行放置一个断点时,它永远不会被击中。这让我认为验证实际上并没有发生。有趣的是,如果我添加一个构造函数,它就会被命中。只是不覆盖。
    • @dn238608:所以客户正在将数据发回给您(因为您的 OP 中并不清楚)?如果是这种情况,我建议将属性用法也更改为包含 Field,因为public IEnumerable&lt;string&gt; AssignedUserGroupIDs { get; set; } 后面有一个支持字段,并且当客户端将数据发回给您时,支持字段被设置。您希望将此验证应用于支持字段。我还更改了AllowMultiple=false,因为没有必要在同一个属性或字段上多次应用此验证。请再次查看我的更新。
    • 抱歉,是的,客户(为了测试我正在使用邮递员)正在发回数据。如果值正确,它会发回我期望的值,但是,我将“-1001”作为值,这应该无法通过验证。即使添加了AttributeTargets.Property | AttributeTargets.Field,我仍然无法在 IsValid 方法中设置断点。
    • 嗯,您可能还想发布您的控制器代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-20
    • 1970-01-01
    • 2012-12-08
    • 2013-01-17
    • 1970-01-01
    相关资源
    最近更新 更多