【问题标题】:How to handle enum as string binding failure when enum value does not parse枚举值不解析时如何将枚举处理为字符串绑定失败
【发布时间】:2022-05-12 20:31:48
【问题描述】:

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

例如。

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

enum SexEnum
{
    Male,
    Female,
    Other
}

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

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

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

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

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

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

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

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

【问题讨论】:

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


    【解决方案1】:

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

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

    【讨论】:

    • 如果这可行,那么我会说它比我的解决方案更好。
    • 谢谢,但如果您阅读了我的问题,我想在没有属性的情况下执行此操作。
    • 对不起。我没有正确地阅读那一点。您如何在系统范围内使用 StringEnumConverter?您可能会从中派生自己的类并覆盖 CanConvert(..) 方法以防止无效值
    • 谢谢。不幸的是,我无法完成这项工作,因为序列化程序吞下了异常,据此无法解决它github.com/aspnet/Mvc/issues/3898
    • 为我工作,而且相当干净。谢谢!
    【解决方案2】:

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

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

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

    【讨论】:

    • 此解决方案适用于复杂类型,如果我的操作只有一个枚举参数怎么办?
    【解决方案3】:

    扩展@Simply Ged 的出色答案,第二部分,使用可为空的枚举产生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);
        }
    }
    

    【讨论】:

      【解决方案4】:

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

      [EnumDataType(typeof(YOUR_ENUM_TYPE))] 高于您的模型枚举。

      例子:

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

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

      【讨论】:

        【解决方案5】:

        您可以使用JsonStringEnumConverter 转换器完成此操作。

        public class PersonModel
        {
            public string Name { get; set; }
        
            [JsonConverter(typeof(JsonStringEnumConverter))]
            public SexKind Sex { get; set; }
        }
        
        public enum SexKind
        {
            Male,
            Female,
            Confused
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-11-18
          相关资源
          最近更新 更多