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
相关产品推荐
相关产品推荐

