枚举值未解析时如何将枚举处理为字符串绑定失败
How to handle enum as string binding failure when enum value does not parse
在我们的 ASP.net Core Web API 应用程序中,我正在寻找一种方法来捕获绑定错误,当我的控制器方法接受一个具有 ENUM 属性 的复杂对象时,ENUM 是de/serialized 作为字符串。
例如
class Person
{
public string Name {get; set;}
public SexEnum Sex {get; set;}
}
enum SexEnum
{
Male,
Female,
Other
}
我们在系统范围内使用 StringEnumConverter
,因此 Person
的 JSON 序列化实例如下所示:
{
"name": "Ann",
"sex": "female"
}
现在如果我 post 这个 JSON (注意 sex
属性 中的拼写错误):
{
"name": "Ann",
"sex": "femal"
}
由于绑定失败,控制器方法接收到的整个对象为 NULL。
我想捕获该绑定错误,而不是让管道进入控制器,就好像没有任何问题一样,return 向客户端发送错误请求,包括其中的详细信息 属性 值绑定失败。
我知道我要反序列化的类型,我知道我要反序列化的 属性 类型,我可以看到该值没有解析为该类型。所以我认为必须有一种方法可以向客户提供该详细信息。我只是不知道在哪里以及如何插入它。
我希望解决方案适用于系统范围,以便涵盖所有枚举,而不必将属性放在模型的属性或枚举本身上。 (这是因为我们将 API 模型分发为 nuget 包,不能有任何依赖项。)
我们最近遇到了这个问题,并编写了自己的属性来处理它:
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 class:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddJsonOptions(options =>
{
options.SerializerSettings.Converters.Add(new validEnumConverter());
});
}
跟进 Simply Ged 上面的回答,AFAICS,这实际上无法完成,因为模型绑定异常被吞没了 (https://github.com/aspnet/Mvc/issues/3898)
ModelState
包含模型绑定错误,您可以从中获取一些信息。由于我们目前仅使用 JSON 序列化,因此我最终实现了一个过滤器来检查 JsonSerializationException
的 ModelState
错误。它并不完美,例如。要从 JsonSerializationException
中获取请求的值(绑定失败),您需要解析内部异常消息。
如果有人找到更好的解决方案,我会很高兴听到。
扩展@Simply Ged 的出色回答,第二部分,使用可为 null 的枚举会产生 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);
}
}
最近刚遇到这个问题。
我克服的方法是应用
[EnumDataType(typeof(YOUR_ENUM_TYPE))]
在您的模型枚举之上。
示例:
public class SaladModel
{
[EnumDataType(typeof(SauceTypeEnum))]
public SauceTypeEnum SauceType { get; set; }
}
现在,一旦您 post 将此发送到 WebAPI 端点,它就会被框架验证并 returns 作为 BadRequest。
您可以使用 JsonStringEnumConverter
转换器执行此操作。
public class PersonModel
{
public string Name { get; set; }
[JsonConverter(typeof(JsonStringEnumConverter))]
public SexKind Sex { get; set; }
}
public enum SexKind
{
Male,
Female,
Confused
}
在我们的 ASP.net Core Web API 应用程序中,我正在寻找一种方法来捕获绑定错误,当我的控制器方法接受一个具有 ENUM 属性 的复杂对象时,ENUM 是de/serialized 作为字符串。
例如
class Person
{
public string Name {get; set;}
public SexEnum Sex {get; set;}
}
enum SexEnum
{
Male,
Female,
Other
}
我们在系统范围内使用 StringEnumConverter
,因此 Person
的 JSON 序列化实例如下所示:
{
"name": "Ann",
"sex": "female"
}
现在如果我 post 这个 JSON (注意 sex
属性 中的拼写错误):
{
"name": "Ann",
"sex": "femal"
}
由于绑定失败,控制器方法接收到的整个对象为 NULL。
我想捕获该绑定错误,而不是让管道进入控制器,就好像没有任何问题一样,return 向客户端发送错误请求,包括其中的详细信息 属性 值绑定失败。
我知道我要反序列化的类型,我知道我要反序列化的 属性 类型,我可以看到该值没有解析为该类型。所以我认为必须有一种方法可以向客户提供该详细信息。我只是不知道在哪里以及如何插入它。
我希望解决方案适用于系统范围,以便涵盖所有枚举,而不必将属性放在模型的属性或枚举本身上。 (这是因为我们将 API 模型分发为 nuget 包,不能有任何依赖项。)
我们最近遇到了这个问题,并编写了自己的属性来处理它:
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 class:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddJsonOptions(options =>
{
options.SerializerSettings.Converters.Add(new validEnumConverter());
});
}
跟进 Simply Ged 上面的回答,AFAICS,这实际上无法完成,因为模型绑定异常被吞没了 (https://github.com/aspnet/Mvc/issues/3898)
ModelState
包含模型绑定错误,您可以从中获取一些信息。由于我们目前仅使用 JSON 序列化,因此我最终实现了一个过滤器来检查 JsonSerializationException
的 ModelState
错误。它并不完美,例如。要从 JsonSerializationException
中获取请求的值(绑定失败),您需要解析内部异常消息。
如果有人找到更好的解决方案,我会很高兴听到。
扩展@Simply Ged 的出色回答,第二部分,使用可为 null 的枚举会产生 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);
}
}
最近刚遇到这个问题。 我克服的方法是应用
[EnumDataType(typeof(YOUR_ENUM_TYPE))]
在您的模型枚举之上。
示例:
public class SaladModel
{
[EnumDataType(typeof(SauceTypeEnum))]
public SauceTypeEnum SauceType { get; set; }
}
现在,一旦您 post 将此发送到 WebAPI 端点,它就会被框架验证并 returns 作为 BadRequest。
您可以使用 JsonStringEnumConverter
转换器执行此操作。
public class PersonModel
{
public string Name { get; set; }
[JsonConverter(typeof(JsonStringEnumConverter))]
public SexKind Sex { get; set; }
}
public enum SexKind
{
Male,
Female,
Confused
}