当枚举值不解析

时间:2018-05-16 08:20:16

标签: asp.net-web-api asp.net-core enums model-binding

在我们的ASP.net核心Web API应用程序中,我正在寻找一种方法来捕获绑定错误,当我的控制器方法接受一个复杂的对象时,当ENUM被解除/序列化为字符串时,该对象具有ENUM属性。

例如

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

enum SexEnum
{
    Male,
    Female
}

我们使用系统范围StringEnumConverter,因此Person的JSON序列化实例如下所示:

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

现在,如果我发布此JSON(请注意sex属性中的拼写错误):

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

由于绑定失败,控制器方法接收的整个对象为NULL。

我想捕获该绑定错误,而不是让管道进入控制器,就好像没有任何错误一样,向客户端返回一个BAD REQUEST,包括哪个属性值无法绑定的详细信息。

我知道我试图反序列化的类型,我知道我试图反序列化的属性类型,我可以看到该值不解析为类型。所以我认为必须有一种向客户提供细节的方法。我只是不知道在哪里以及如何插入它。

我希望解决方案是系统范围的,以便覆盖所有枚举,而不必将属性放在模型的属性或枚举本身上。 (这是因为我们将API模型作为nuget包分发,不能有任何依赖。)

4 个答案:

答案 0 :(得分:4)

我们最近遇到了这个问题并编写了我们自己的属性来处理它:

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

修改

如果您不想使用属性,那么您可以从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());
    });
}

答案 1 :(得分:0)

跟随Simply Ged上面的回答AFAICS,实际上无法完成,因为模型绑定异常被吞下(https://github.com/aspnet/Mvc/issues/3898

ModelState包含模型绑定错误,您可以从中获取一些信息。由于我们目前仅使用JSON序列化,因此我最终实现了一个过滤器来检查ModelState的{​​{1}}错误。尽管如此,它并不完美。从您需要解析内部异常消息的JsonSerializationException中获取请求的值(绑定失败)。

如果有人找到更好的解决方案,我会很高兴听到。

答案 2 :(得分:0)

使用可为空的枚举扩展@Simply Ged的出色答案(第二部分)会产生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); } } 异常。需要一个额外的步骤来处理可为空的枚举或枚举的空值:

image2 = Image.open('media/' + str(image.file))
width, height = image2.size;
image2 = ImageOps.expand(image2, border=(int(width/25),int(height/20),int(width/25),int(height/10)), fill='rgb(0,0,0)')

答案 3 :(得分:0)

最近刚遇到这个问题。 我克服的方法是应用

[EnumDataType(typeof(YOUR_ENUM_TYPE))] 在您的模型枚举上方。

示例:

public class SaladModel
{
   [EnumDataType(typeof(SauceTypeEnum))]
   public SauceTypeEnum SauceType { get; set; }
}

现在,一旦您将其发布到 WebAPI 端点,框架就会对其进行验证并作为 BadRequest 返回。