当枚举值不解析时如何将枚举视为字符串绑定失败

Vik*_*tor 6 enums model-binding asp.net-web-api asp.net-core

在我们的ASP.net Core Web API应用程序中,当我的控制器方法接受将ENUMs反序列化为字符串的ENUM属性的复杂对象时,我正在寻找一种捕获绑定错误的方法。

例如。

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

enum SexEnum
{
    Male,
    Female
}
Run Code Online (Sandbox Code Playgroud)

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

{
    "name": "Ann",
    "sex": "female"
}
Run Code Online (Sandbox Code Playgroud)

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

{
    "name": "Ann",
    "sex": "femal"
}
Run Code Online (Sandbox Code Playgroud)

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

我想捕获该绑定错误,而不是让管道像没有什么错误一样进入控制器,而是向客户端返回一个BAD REQUEST,其中包括未能绑定哪个属性值的详细信息。

我知道我要反序列化的类型,我知道我要反序列化的属性类型,我可以看到该值未解析为该类型。因此,我认为必须有一种方法可以将详细信息提供给客户。我只是不知道在哪里以及如何插入它。

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

Sim*_*Ged 6

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

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;}
}
Run Code Online (Sandbox Code Playgroud)

然后将错误添加到ModelState中,以便您可以ModelState.IsValid检查值是否有效。

if(!ModelState.IsValid)
{
    return BadRequest(ModelState);
}
Run Code Online (Sandbox Code Playgroud)

编辑

如果您不想使用属性,则可以从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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这将添加到启动类的JsonOptions中:

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

  • 谢谢。不幸的是,由于序列化器吞没了异常,因此我无法使这项工作生效,并且据此无法解决https://github.com/aspnet/Mvc/issues/3898 (2认同)

Vik*_*tor 3

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

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

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