Перечислить как обязательное поле в ASP.NET Core WebAPI - PullRequest
0 голосов
/ 15 января 2019

Можно ли вернуть сообщение об ошибке атрибута [Required], если в запросе JSON не указано правильное значение для свойства enum?

Например, у меня есть модель для сообщения POST, которое содержит свойство AddressType, которое является типом перечисления:

public class AddressPostViewModel
{
    [JsonProperty("addressType")]
    [Required(ErrorMessage = "Address type is required.")]
    public AddressType AddressType { get; set; }
}

Перечисление AddressType принимает два значения:

[JsonConverter(typeof(StringEnumConverter))]
public enum AddressType
{
    [EnumMember(Value = "Dropship")]
    Dropship,
    [EnumMember(Value = "Shipping")]
    Shipping
}

Я заметил (или на самом деле моя команда QA заметила), что если сообщение запроса JSON содержит пустую строку или ноль для AddressType, сообщение об ошибке не является ожидаемым Address type is required. сообщением. Вместо этого сообщение об ошибке является несколько недружественной ошибкой синтаксического анализа.

Например, если запрос JSON выглядит так:

{  "addressType": "" }

Тогда ошибка, автоматически генерируемая платформой проверки, выглядит следующим образом:

{
    "message": "Validation Failed",
    "errors": [
        {
            "property": "addressType",
            "message": "Error converting value \"\" to type 'MyNamespace.AddressType'. Path 'addressType', line 4, position 19."
        }
    ]
}

Есть ли способ обеспечить возвращение сообщения об ошибке атрибута [Required], если кто-то не включает допустимое значение для перечисления?

Ответы [ 3 ]

0 голосов
/ 15 января 2019

Я не думаю, что есть готовая аннотация данных, проверяющая значения перечисления.

Вы можете получить из обязательного атрибута, хотя:

using System;
using System.ComponentModel.DataAnnotations;

    public class RequiredEnumAttribute : RequiredAttribute
    {
        public override bool IsValid(object value)
        {
            if (value == null) return false;
            var type = value.GetType();
            return type.IsEnum && Enum.IsDefined(type, value);
        }
}

Метод Enum.IsDefined проверяет, существует ли данный value в перечислении данного type.

Использование:

[RequiredEnum(ErrorMessage = "Your error message.")]
public YourEnum EnumProperty { get; set; }

См. эту статью.

0 голосов
/ 15 января 2019

Я придумала решение, отвечающее моим требованиям, хотя код заставляет меня немного съеживаться.

Я сохранил атрибут [Required] для свойства AddressType в модели представления. Неприятная часть состоит в том, что я должен был сделать собственность nullable:

public class AddressPostViewModel
{
    [JsonProperty("addressType")]
    [Required(ErrorMessage = "Address type is required.")]
    public AddressType? AddressType { get; set; }
}

В самом перечислении AttributeType я заменил атрибут StringEnumConverter на пользовательский JsonConverter, как предлагает @Manoj Choudhari:

[JsonConverter(typeof(CustomStringToEnumConverter))]
public enum AddressType
{
    [EnumMember(Value = "Dropship")]
    Dropship,
    [EnumMember(Value = "Shipping")]
    Shipping
}

Это CustomStringToEnumConverter:

public class CustomStringToEnumConverter : StringEnumConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (string.IsNullOrEmpty(reader.Value?.ToString()))
        {
            return null;
        }

        object parsedEnumValue;

        var isValidEnumValue = Enum.TryParse(objectType.GenericTypeArguments[0], reader.Value.ToString(), true, out parsedEnumValue);

        if (isValidEnumValue)
        {
            return parsedEnumValue;
        }
        else
        {
            return null;
        }
    }
}

CustomerStringToEnumConverter может обрабатывать пустые строки, нули и недопустимые строки. Если он обнаруживает недопустимое значение перечисления, он возвращает null, который затем перехватывается, когда происходит обязательное подтверждение поля (магия), и в ответе JSON отображается сообщение об ошибке RequiredAttribute.

Хотя мне не нравится делать тип AttributeType обнуляемым, потребитель моего API увидит непротиворечивое сообщение проверки, если значение AttributeType неверно в запросе JSON.

0 голосов
/ 15 января 2019

Вариант 1: использовать пользовательский атрибут RequiredEnum и избегать атрибута JsonConverter

Не помещайте JsonConverter в перечисление AddressType. Этот StringToEnum не может отобразить строку. Пустое значение для перечисления, и он выдает это сообщение об ошибке.

Вместо этого вы можете написать собственный требуемый валидатор перечисления, как показано ниже.

    using System;
    using System.ComponentModel.DataAnnotations;

    public class RequiredEnumFieldAttribute: RequiredAttribute
    {
        public override bool IsValid(object value)
        {
            if (value == null)
            {
                 return false;
            }

            var type = value.GetType();
            return type.IsEnum && Enum.IsDefined(type, value);
        }
   }

Тогда вы можете использовать его, как показано ниже:

public class AddressPostViewModel
{
    [JsonProperty("addressType")]
    [RequiredEnumField(ErrorMessage = "Address type is required.")]
    public AddressType AddressType { get; set; }
}

Вариант 2. Использование настраиваемого JsonConverter для AddressType

Добавьте один пользовательский CustomStringToEnumConverter, производный от StringEnumConverter.

Этот метод выдаст ошибку, если значение в JSON будет пустым.

public class CustomStringToEnumConverter : StringEnumConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (string.IsNullOrEmpty(reader.Value.ToString()))
            throw new Exception("Address not provided");

        return base.ReadJson(reader, objectType, existingValue, serializer);
    }
}

Используйте этот jsonConverter вместо StringEnumConverter по умолчанию, как показано ниже

[JsonConverter(typeof(CustomStringToEnumConverter))]
public enum AddressType
{
}
...