Пользовательский атрибут проверки не проверяется в модели - WebAPI C# и JSON - PullRequest
1 голос
/ 14 июля 2020

Я создал модель и настраиваемый атрибут проверки, чтобы гарантировать, что значение больше нуля. Проблема, с которой я столкнулся, заключается в том, что конструктор настраиваемого атрибута поражен, но переопределение 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; }
}

Прямо сейчас клиент установит AssignedUserGroupIDs в JSON. Я хотел бы подтвердить, что каждый из идентификаторов в этой коллекции больше нуля.

Вот образец 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);
        }

1 Ответ

0 голосов
/ 14 июля 2020

Если вы хотите проверить, что каждый из идентификаторов в коллекции больше нуля, вам нужно преобразовать value как IEnumerable, а затем l oop через каждый элемент в списке:

/*
 * 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;
    }
}

Снимок экрана, демонстрирующий попадание:

введите описание изображения здесь

...