简体   繁体   English

自定义验证属性未在 Model 上验证 - WebAPI C# 和 JSON

[英]Custom Validation Attribute not validating on Model - WebAPI C# and JSON

I have created a Model and a custom validation attribute to ensure that the value is greater than zero.我创建了一个 Model 和一个自定义验证属性,以确保该值大于零。 The issue that I'm having is that the constructor of the custom attribute is hit, but the IsValid override is never hit.我遇到的问题是自定义属性的构造函数被命中,但 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; }
}

Right now, the client will set the AssignedUserGroupIDs in JSON.现在,客户端将在 JSON 中设置AssignedUserGroupIDs I'd like to validate that each of the IDs in that collection are greater than zero.我想验证该集合中的每个 ID 是否大于零。

Here is sample JSON for the request:这是请求的示例 JSON:

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

For what it's worth, I'm using JsonOptions to use camel casing for the property names and to covert string to enums.对于它的价值,我使用 JsonOptions 来使用驼峰式大小写作为属性名称并将字符串转换为枚举。 I'm only mentioning this because I'm not sure if the Json is causing any issues or not.我之所以提到这一点,是因为我不确定 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;
}

Here is the Controller:这是 Controller:

        [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);
        }

If you would like to validate each of the IDs in the collection is greater than zero, then you need to cast the value as IEnumerable , and then loop through each item in the list:如果您想验证集合中的每个 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;
    }
}

Screenshot to show it's being hit:显示它被击中的屏幕截图:

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM