You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core如何仅允许字符串枚举,禁止传入整数枚举值?

禁止API接收整数形式的枚举值(仅允许字符串枚举)

核心问题分析

你配置的JsonStringEnumConverter(allowIntegerValues: false)未能完全拦截整数转枚举,是因为默认的枚举反序列化逻辑可能仍在生效,或者转换器优先级不足。要实现严格的字符串枚举校验,需要从JSON反序列化和**模型绑定(针对Query/Form参数)**两个层面彻底覆盖默认行为。

解决方案步骤

1. 自定义严格字符串枚举转换器(处理JSON请求)

创建自定义JsonConverterFactory和JsonConverter<TEnum>,强制仅接受字符串输入,直接拒绝整数类型的枚举值:

public class StrictStringEnumConverterFactory : JsonConverterFactory
{
    private readonly JsonNamingPolicy _namingPolicy;

    public StrictStringEnumConverterFactory(JsonNamingPolicy namingPolicy)
    {
        _namingPolicy = namingPolicy;
    }

    public override bool CanConvert(Type typeToConvert)
    {
        return typeToConvert.IsEnum;
    }

    public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
    {
        var converterType = typeof(StrictStringEnumConverter<>).MakeGenericType(typeToConvert);
        return (JsonConverter)Activator.CreateInstance(converterType, _namingPolicy);
    }

    private class StrictStringEnumConverter<TEnum> : JsonConverter<TEnum> where TEnum : struct, Enum
    {
        private readonly Dictionary<string, TEnum> _enumMap;

        public StrictStringEnumConverter(JsonNamingPolicy namingPolicy)
        {
            _enumMap = Enum.GetValues<TEnum>()
                .ToDictionary(e => namingPolicy.ConvertName(e.ToString()), e => e, StringComparer.OrdinalIgnoreCase);
        }

        public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            if (reader.TokenType != JsonTokenType.String)
            {
                throw new JsonException($"枚举 {typeof(TEnum).Name} 仅接受字符串值,当前输入类型为 {reader.TokenType}");
            }

            var value = reader.GetString();
            if (!_enumMap.TryGetValue(value, out var enumValue))
            {
                throw new JsonException($"枚举 {typeof(TEnum).Name} 不支持值 '{value}'");
            }

            return enumValue;
        }

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

然后在AddJsonOptions中替换默认转换器:

return builder.AddJsonOptions(options =>
{
    var namingPolicy = JsonNamingPolicy.CamelCase;

    // 移除默认的字符串枚举转换器(如果存在)
    var defaultEnumConverter = options.JsonSerializerOptions.Converters
        .FirstOrDefault(c => c.GetType() == typeof(JsonStringEnumConverter));
    if (defaultEnumConverter != null)
    {
        options.JsonSerializerOptions.Converters.Remove(defaultEnumConverter);
    }

    // 添加自定义严格枚举转换器
    options.JsonSerializerOptions.Converters.Add(new StrictStringEnumConverterFactory(namingPolicy));
});

2. 自定义模型绑定器(处理Query/Form参数)

如果API允许通过QueryString或FormData传递枚举值,默认模型绑定会自动解析整数,需要添加自定义绑定器强制仅接受字符串:

// 模型绑定器提供器
public class StrictEnumModelBinderProvider : IModelBinderProvider
{
    private readonly JsonNamingPolicy _namingPolicy;

    public StrictEnumModelBinderProvider(JsonNamingPolicy namingPolicy)
    {
        _namingPolicy = namingPolicy;
    }

    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context.Metadata.ModelType.IsEnum)
        {
            var binderType = typeof(StrictEnumModelBinder<>).MakeGenericType(context.Metadata.ModelType);
            return (IModelBinder)Activator.CreateInstance(binderType, _namingPolicy);
        }

        return null;
    }
}

// 具体的枚举绑定器
public class StrictEnumModelBinder<TEnum> : IModelBinder where TEnum : struct, Enum
{
    private readonly JsonNamingPolicy _namingPolicy;

    public StrictEnumModelBinder(JsonNamingPolicy namingPolicy)
    {
        _namingPolicy = namingPolicy;
    }

    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if (valueProviderResult == ValueProviderResult.None)
        {
            return Task.CompletedTask;
        }

        var value = valueProviderResult.FirstValue;
        if (string.IsNullOrEmpty(value))
        {
            bindingContext.ModelState.AddModelError(bindingContext.ModelName, $"枚举 {bindingContext.ModelName} 值不能为空");
            return Task.CompletedTask;
        }

        var enumMap = Enum.GetValues<TEnum>()
            .ToDictionary(e => _namingPolicy.ConvertName(e.ToString()), e => e, StringComparer.OrdinalIgnoreCase);

        if (!enumMap.TryGetValue(value, out var enumValue))
        {
            bindingContext.ModelState.AddModelError(bindingContext.ModelName, $"枚举 {typeof(TEnum).Name} 不支持值 '{value}'");
            return Task.CompletedTask;
        }

        bindingContext.Result = ModelBindingResult.Success(enumValue);
        return Task.CompletedTask;
    }
}

注册绑定器到服务:

builder.Services.AddControllers(options =>
{
    // 插入到绑定器列表最前面,确保优先级最高
    options.ModelBinderProviders.Insert(0, new StrictEnumModelBinderProvider(JsonNamingPolicy.CamelCase));
});

效果验证

完成配置后:

  • JSON请求中传入整数枚举值会直接抛出JsonException,返回400错误
  • Query/Form参数中传入整数或无效字符串会触发模型验证错误,返回包含错误信息的400响应

内容的提问来源于stack exchange,提问作者karczilla

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 00:30:55