枚举流利验证只接受字符串

Enum fluent validation to accept only string

下面是我的枚举

public enum IdentifierType
{
    Customer = 1,
    Manager = 2,
    Director = 3
}

在 .Net 核心中使用流畅的验证,是否可以在请求中传递 1/"1" 或 2/"2" 或 3/"3" 时进行验证 return 验证错误?

传递“客户”或“经理”等应该没问题。

我知道在 C# 中枚举类型是 'int',但是首先考虑一下这是否可行?

startup.cs 中的设置:Validator 在我的转换器之前注册。

services.AddControllers()
        .AddFluentValidation(configuration =>
        {
          configuration.RegisterValidatorsFromAssemblyContaining<Startup>();
        })
          .ConfigureApiBehaviorOptions(opt => { opt.SuppressModelStateInvalidFilter = true; })
          .AddJsonOptions(serializerOption =>
          {
            serializerOption.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
            serializerOption.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
            serializerOption.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
          });

API 使用邮递员的附加行为

MVC 框架必须将 JSON 转换为 class 才能使 FluentValidation 正常工作。

但是,您可以配置 MVC 以先对其进行验证。

要允许任何 ASP.NET MVC 核心应用程序中的 built-in JSON 序列化程序允许将字符串转换为枚举,您需要添加 JsonStringEnumConverter 转换器在您的应用启动时。此转换器还有一个参数,您可以将其设置为 false 以禁止使用整数值。例如:

services
    .AddMvc()
    .AddJsonOptions(opts =>
    {
        opts.JsonSerializerOptions.Converters.Add(
            new JsonStringEnumConverter(allowIntegerValues: false));
    })

由于 '' 似乎不会阻止将 int 作为字符串传递,因此您可以编写自己的转换器。例如,像这样的东西会起作用:

public class IdentifierTypeConverter : JsonConverter<IdentifierType>{
    public override IdentifierType Read(ref Utf8JsonReader reader, 
        Type typeToConvert, JsonSerializerOptions options)
    {
        var value = reader.GetString();
        
        if(value == null)
        {
            throw new Exception("No null values thanks!");  
        }
        
        if(int.TryParse(value, out var _))
        {
            throw new Exception("No numbers thanks!");
        }
        
        return (IdentifierType)Enum.Parse(typeof(IdentifierType), value);
    }

    public override void Write(Utf8JsonWriter writer, IdentifierType value, 
        JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString());
    }
}

您可以使用 JsonConverterFactory 制作通用版本。首先使转换器通用:

public class EnumConverter<TEnum> : JsonConverter<TEnum> where TEnum : Enum
{
 public override bool CanConvert(Type typeToConvert) => typeToConvert.IsEnum;

    public override TEnum Read(ref Utf8JsonReader reader, 
        Type typeToConvert, JsonSerializerOptions options)
    {
        var value = reader.GetString();

        if (value == null)
        {
            throw new Exception("No null values thanks!");
        }

        if (int.TryParse(value, out var _))
        {
            throw new Exception("No numbers thanks!");
        }

        return (TEnum)Enum.Parse(typeof(TEnum), value);
    }

    public override void Write(Utf8JsonWriter writer, TEnum value, 
        JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString());
    }
}

并让你的工厂:

public class EnumConverterFactory : JsonConverterFactory
{
    public override bool CanConvert(Type typeToConvert) => typeToConvert.IsEnum;

    public override JsonConverter? CreateConverter(Type typeToConvert, 
        JsonSerializerOptions options)
    {
        
        JsonConverter converter = (JsonConverter)Activator.CreateInstance(
            typeof(EnumConverter<>).MakeGenericType(typeToConvert))!;

        return converter;
    }
}

现在添加工厂:

opts.JsonSerializerOptions.Converters.Add(new EnumConverterFactory());

据我所知,流畅的验证没有直接的解决方案来解决您的问题。但是,我会按如下所述处理这种情况:

在下面的代码中,我创建了两个属性 - IdentifierType 字段是 public,它将接受来自您的客户端的字符串值。另一个字段是 enum,它可以在您的项目内部使用。

public class YourRequestModel
{
   internal IdentifierType IdType
   {
     get
     {
        return Enum.Parse<IdentifierType>(IdentifierType);
     }
    }
   public string IdentifierType { get; }
}

在进行流畅验证时,仅根据字符串值验证输入。

public class YourRequestModelValidator: AbstractValidator<YourRequestModel> 
{
  RuleFor(x => x.IdentifierType)
            .IsEnumName(typeof(IdentifierType), caseSensitive: false);
}