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

如何用ValidationAttribute或IModelBinder确保枚举属性必填且值有效?

问题描述

现有代码

枚举与模型类

public enum Types
{
    Emergency,
    Notice,
    Event
}

public class Message
{
    public Types Type { get; set; }
    public string Title { get; set; }
}

控制器端点

[HttpPost]
public async Task<ActionResult> CreateMessage(Message newMessage)
{
    return Ok();
}

当前问题

  • 当JSON请求体包含有效值(如字符串"emergency"或数字1)时,接口正常返回200响应;
  • 当JSON请求体不包含Type字段、Type值为"spam"或数字5时,期望返回400响应,但.NET默认模型绑定会把未传入的枚举属性设为默认值0(对应Emergency),导致[Required]特性完全失效。

注:已实现自定义JsonConvertor完成字符串到枚举值的转换;无法将Type设为可空类型(属性为必填项),也不能修改枚举起始值为1;希望仅对该属性应用特殊绑定/验证逻辑,而非全局生效。

需求

实现以下两个验证规则:

  1. JSON请求体中必须包含Type枚举属性;
  2. Type属性值必须属于枚举定义的有效范围。

解决方案

方法一:自定义ValidationAttribute

针对属性单独验证,无需修改模型绑定逻辑,直接在属性上标记特性即可。

自定义验证特性代码

public class RequiredEnumAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        // 获取当前请求上下文
        var httpContext = validationContext.GetService<IHttpContextAccessor>()?.HttpContext;
        if (httpContext != null)
        {
            // 重置请求体流位置,确保能完整读取
            httpContext.Request.Body.Position = 0;
            using var reader = new StreamReader(httpContext.Request.Body);
            var jsonBody = reader.ReadToEnd();
            var jsonObj = JsonSerializer.Deserialize<JsonElement>(jsonBody);
            
            // 检查Type字段是否存在
            if (!jsonObj.TryGetProperty("Type", out _))
            {
                return new ValidationResult("请求体必须包含Type字段");
            }
        }

        // 验证枚举值是否在定义范围内
        var enumType = value.GetType();
        if (!Enum.IsDefined(enumType, value))
        {
            return new ValidationResult($"Type值无效,有效值为:{string.Join(", ", Enum.GetNames(enumType))}");
        }

        return ValidationResult.Success;
    }
}

模型类修改

在Type属性上添加自定义特性:

public class Message
{
    [RequiredEnum]
    public Types Type { get; set; }
    public string Title { get; set; }
}

配置说明

需要在Startup/Program.cs中注册IHttpContextAccessor服务:

builder.Services.AddHttpContextAccessor();

方法二:自定义IModelBinder(单个属性生效)

直接控制属性的绑定过程,同时完成必填检查和有效值验证,仅对标记的属性生效。

自定义模型绑定器代码

public class RequiredEnumModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        var httpContext = bindingContext.HttpContext;
        // 重置请求体流位置
        httpContext.Request.Body.Position = 0;
        using var reader = new StreamReader(httpContext.Request.Body);
        var jsonBody = reader.ReadToEnd();
        var jsonObj = JsonSerializer.Deserialize<JsonElement>(jsonBody);

        // 规则1:检查请求体是否包含Type字段
        if (!jsonObj.TryGetProperty("Type", out var typeElement))
        {
            bindingContext.ModelState.AddModelError(bindingContext.ModelName, "请求体必须包含Type字段");
            return Task.CompletedTask;
        }

        // 规则2:验证枚举值是否有效
        try
        {
            var enumValue = Enum.Parse(typeof(Types), typeElement.ToString(), ignoreCase: true);
            if (!Enum.IsDefined(typeof(Types), enumValue))
            {
                throw new ArgumentException();
            }
            bindingContext.Result = ModelBindingResult.Success(enumValue);
        }
        catch
        {
            bindingContext.ModelState.AddModelError(bindingContext.ModelName, $"Type值无效,有效值为:{string.Join(", ", Enum.GetNames(typeof(Types)))}");
        }

        return Task.CompletedTask;
    }
}

模型类修改

在Type属性上标记绑定器:

public class Message
{
    [ModelBinder(BinderType = typeof(RequiredEnumModelBinder))]
    public Types Type { get; set; }
    public string Title { get; set; }
}

控制器优化(可选)

在控制器中主动检查模型状态,返回标准400响应:

[HttpPost]
public async Task<ActionResult> CreateMessage(Message newMessage)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }
    return Ok();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 07:18:10