Как обрабатывать перечисление как сбой привязки строки, когда значение перечисления не анализируется - PullRequest
0 голосов
/ 16 мая 2018

В нашем приложении ASP.net Core Web API я ищу способ отловить ошибки привязки, когда мой метод контроллера принимает сложный объект, который имеет свойство ENUM, когда ENUM де / сериализуются как строки.

например.

class Person
{
    public string Name {get; set;}
    public SexEnum Sex {get; set;}
}

enum SexEnum
{
    Male,
    Female
}

Мы используем всю систему StringEnumConverter, поэтому сериализованный JSON экземпляр Person выглядит так:

{
    "name": "Ann",
    "sex": "female"
}

Теперь, если я опубликую этот JSON (обратите внимание на опечатку в свойстве sex):

{
    "name": "Ann",
    "sex": "femal"
}

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

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

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

Я бы хотел, чтобы решение было общесистемным, чтобы охватить все перечисления без необходимости устанавливать атрибуты в свойствах модели или самих перечислений. (Это потому, что мы распространяем наши модели API в виде пакета nuget, который не может иметь никаких зависимостей.)

Ответы [ 3 ]

0 голосов
/ 29 мая 2018

В ответ на ответ Просто Геда, AFAICS, это на самом деле невозможно, так как исключения привязки модели проглочены (https://github.com/aspnet/Mvc/issues/3898)

ModelState содержит ошибки привязки модели, и вы можете получить некоторую информацию из этого. Поскольку в настоящее время мы используем только сериализацию JSON, я реализовал фильтр для проверки ошибок ModelState для JsonSerializationException. Это не идеально, хотя, как, например. чтобы получить запрошенное значение (не прошедшее привязку) из JsonSerializationException, вам необходимо проанализировать внутреннее сообщение об исключении.

Если кто-то найдет лучшее решение, я буду рад услышать.

0 голосов
/ 26 июля 2018

Расширение на превосходный ответ @Simply Ged, 2-я часть, с использованием нуля enum приводит к исключению System.ArgumentException: 'Type provided must be an Enum.'. Для обработки нумерованного перечисления требуется дополнительный шаг или нулевое значение для перечисления:

public class validEnumConverter : StringEnumConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        Type enumType = (Nullable.GetUnderlyingType(objectType) ?? objectType);
        if(!Enum.IsDefined(enumType, reader.Value ?? string.Empty))
        {
            throw new ArgumentException("Invalid enum value");
        }

        return base.ReadJson(reader, objectType, existingValue, serializer);
    }
}
0 голосов
/ 16 мая 2018

У нас недавно возникла эта проблема, и мы создали собственный атрибут для ее обработки:

public class ValidEnumValueAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        Type enumType = value.GetType();
        bool valid = Enum.IsDefined(enumType, value);

        if(!valid)
        {
            return new ValidationResult($"{value} is not a valid value for type {enumType.Name}");
        }

        return ValidationResult.Success;
    }
}

class Person
{
    public string Name {get; set;}

    [ValidEnumValue]
    public SexEnum Sex {get; set;}
}

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

if(!ModelState.IsValid)
{
    return BadRequest(ModelState);
}

EDIT

Если вы не хотите использовать атрибут, вы можете получить новый конвертер из NewtonSoft StringEnumConverter и проверить это значениедопустимо перед чтением json, например

public class validEnumConverter : StringEnumConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if(!Enum.IsDefined(objectType, reader.Value))
        {
            throw new ArgumentException("Invalid enum value");
        }

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

Это добавлено к JsonOptions в вашем классе запуска:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().AddJsonOptions(options =>
    {
        options.SerializerSettings.Converters.Add(new validEnumConverter());
    });
}
...