排坑日记:ASP.NET Core 中 “Required field is not provided“ 验证错误全记录

简介: ASP.NET Core 10.0 中,`[ApiController]` 对非可空 `int` 路由参数自动添加 `[Required]`,但多 `[FromRoute]` 场景下验证管道误判绑定来源,导致“Required field is not provided”假错。根治方案:改用 `[FromBody]` DTO(如 `InventoryDecrementDto`),符合 REST 规范,验证稳定、提示友好。

问题出现

在开发一个基于 ASP.NET Core 10.0 的 Web API 项目时,遇到了一个令人困惑的验证错误。

库存扣减接口的原始代码如下:

[HttpPost("{drugId}/decrement/{quantity}")]
public async Task<string?> DecInventoryAsync(
    [FromRoute] string drugId,
    [FromRoute] int quantity)
{
   
    var inventory = await _inventoryService.DeductInventoryAsync(drugId, quantity);
    return inventory;
}

通过 Swagger UI 调用接口:

POST /api/inventories/drug001/decrement/10

返回的却是 400 错误:

{
   
    "errors": {
   
        "quantity": ["Required field is not provided."]
    }
}

这就很奇怪了——URL 路径里明明写着 decrement/10,quantity 的值 10 就在那里,怎么说没提供?


问题排查

既然路由拼写和参数绑定看起来都没问题,那就一步步缩小范围。

第一步:排除 [FromRoute] 显式标记的影响

假设:[FromRoute] 显式标注与 [ApiController] 的自动推断产生了冲突。

验证:去掉 [FromRoute],让框架自动推断绑定来源:

[HttpPost("{drugId}/decrement/{quantity}")]
public async Task<string?> DecInventoryAsync(string drugId, int quantity)

结果:依然报错 "Required field is not provided." → 假设不成立,问题不在 [FromRoute]。

第二步:排查参数类型的影响

假设:非可空值类型 int 被 [ApiController] 隐式标记为 [Required],导致验证管道的误判。

验证:将 int quantity 改为可空值类型 int?:

public async Task<string?> DecInventoryAsync(
    [FromRoute] string drugId,
    [FromRoute] int? quantity)

结果:错误消失了!→ 假设成立。

关键发现:问题出在 int 这个非可空值类型上。[ApiController] 对非可空值类型会自动添加 [Required] 验证,而在多参数 [FromRoute] 绑定场景下,验证管道无法正确识别该参数已被路由提供,于是报了误判错误。

第三步:确认根因

翻阅官方文档,[ApiController] 的隐式规则总结如下:

  1. 对非可空值类型自动添加 [Required] 验证
  2. 对所有参数进行自动模型验证
  3. 当存在多个 [FromRoute] 参数时,原生 AddOpenApi() 生成的 OpenAPI schema 和模型验证管道之间可能存在判断不一致

根因确诊:[ApiController] 自动验证 + int 非可空类型 + 多 [FromRoute] 参数 + AddOpenApi() 四者叠加,触发了验证管道的兼容性边界 bug。


可选修复方案

确定了根因,接下来评估修复方案。核心思路是打破触发条件中的任意一环:

方案 做法 打破的条件 评估
① 可空类型 int → int? 非可空类型 → 不触发隐式 [Required] 改动最小,但需手动校验,且路由参数用 int? 语义不自然
② 去掉 [FromRoute] 不显式标注绑定来源 去掉显式标记 已验证无效,不可采用
③ [AsParameters] 用 Record 封装多参数 让验证管道正确识别绑定来源 有效,但需额外定义类型,且路由风格未改善
④ [BindRequired] 用显式绑定替代隐式 [Required] 替换隐式 Required 治标不治本,未来可能再踩坑
⑤ 请求体 DTO 参数从路由移至 Body 彻底消除多 [FromRoute] 场景 RESTful 规范,验证管道成熟稳定,错误提示友好

选择方案 ⑤ 的理由:

  • 从根源上消除了"多 [FromRoute] 参数"这个触发条件
  • POST 请求将业务参数放在 Body 中更符合 RESTful 设计规范
  • [FromBody] 的验证管道非常成熟,不会再出现此类边界问题
  • 可以配合 [Required]、[Range] 等特性提供友好的中文错误提示

最终修复:使用 InventoryDecrementDto 请求体

新增 DTO 对象

// Models/Dto/InventoryDecrementDto.cs
using System.ComponentModel.DataAnnotations;

namespace MyWebApp.Models.Dto;

public class InventoryDecrementDto
{
   
    [Required(ErrorMessage = "药品ID不能为空")]
    public string DrugId {
    get; set; }

    [Required(ErrorMessage = "库存数量不能为空")]
    [Range(1, int.MaxValue, ErrorMessage = "库存数量必须大于0")]
    public int Quantity {
    get; set; }
}

修改控制器

// 修改前
[HttpPost("{drugId}/decrement/{quantity}")]
public async Task<string?> DecInventoryAsync(
    [FromRoute] string drugId,
    [FromRoute] int quantity)

// 修改后
[HttpPost("decrement")]
public async Task<string?> DecInventoryAsync(InventoryDecrementDto decrementDto)
{
   
    var inventory = await _inventoryService.DeductInventoryAsync(
        decrementDto.DrugId,
        decrementDto.Quantity);
    return inventory;
}

新的接口签名

POST /api/inventories/decrement
Content-Type: application/json

{
    "drugId": "drug001",
    "quantity": 10
}

修复效果对比

对比维度 修复前 修复后
路由风格 POST /{drugId}/decrement/{quantity} POST /decrement + JSON Body
参数绑定 [FromRoute] × 2(触发 bug) [FromBody](默认,稳定)
验证行为 误判 "Required field is not provided." 正常校验,中文错误提示
不传 drugId 误判为 quantity 缺失 返回 "药品ID不能为空"
不传 quantity 误判为 quantity 缺失 返回 "库存数量不能为空"
quantity 为 0 误判为 quantity 缺失 返回 "库存数量必须大于0"

问题根因

[ApiController] 自动验证
       +
非可空值类型 int → 隐式添加 [Required]
       +
多个 [FromRoute] 参数
       +
.NET 原生 AddOpenApi() 的 schema 生成逻辑
       ↓
验证管道无法正确识别路由参数的绑定来源
       ↓
"Required field is not provided."(误报)

核心矛盾:[ApiController] 对 int 自动加了 [Required],但 AddOpenApi() 在处理多 [FromRoute] 参数时,生成的 OpenAPI schema 与验证管道的判断逻辑不一致,导致验证管道认为 quantity 没有被提供。


经验总结

  1. "Required field is not provided" 不一定是真的没传参数——遇到这个错误,先检查是不是非可空值类型 + 多来源绑定的组合触发了框架的边界 bug。

  2. 排查这类问题最快的切入点是改类型:把 int 改成 int? 试一下,如果错误消失,问题就在非可空类型的隐式 [Required] 上。

  3. POST 请求的参数放 Body 是最稳妥的做法:[FromBody] 的模型绑定和验证管道是 ASP.NET Core 中最成熟、踩坑最少的路径,能避免大量路由参数绑定的边界问题。

  4. DTO 模式一举两得:既解决了验证问题,又让 API 设计更规范,还方便了参数校验和 OpenAPI 文档生成。

目录
相关文章
|
Linux Python
LINUX These critical programs are missing or too old: compiler python
LINUX These critical programs are missing or too old: compiler python
1719 0
LINUX These critical programs are missing or too old: compiler python
|
5月前
|
消息中间件 Linux Docker
Windows Docker Desktop 环境下 RabbitMQ 生产级部署完整指南
本文详解Windows下基于Docker Desktop部署RabbitMQ的标准化方案:避开Erlang配置痛点,推荐`rabbitmq:3.13-management-alpine`镜像;涵盖极简测试、持久化部署(必设`--hostname`)、自定义配置三类方案,并重点梳理Windows特有坑点(BOM编码、CRLF换行、guest远程限制等),助力快速搭建稳定本地开发环境。
607 7
|
5月前
|
JSON 自然语言处理 中间件
.NET 10 OpenAPI 三剑客:Swashbuckle、NSwag 与 Scalar 实战对比
本文对比.NET 10中Swashbuckle、NSwag与Scalar三大OpenAPI UI工具的现代化集成方案。自.NET 9起,文档生成已内置(`AddOpenApi`/`MapOpenApi`),UI工具仅负责渲染同一份`/openapi/v1.json`,实现解耦。三者注册方式、UI体验与扩展能力各具特色,选型应基于团队习惯、设计需求及生态整合目标。
266 0
.NET 10 OpenAPI 三剑客:Swashbuckle、NSwag 与 Scalar 实战对比
|
5月前
|
关系型数据库 MySQL 测试技术
升级 .NET 10 不想等 Pomelo.EntityFrameworkCore.MySql?试试 Microting.EntityFrameworkCore.MySql
本文详解如何在Pomelo尚未支持EF Core 10时,平滑升级至.NET 10:介绍Microting——Pomelo的官方分支,专注快速适配新.NET版本,API高度兼容,仅需替换命名空间与NuGet包即可完成迁移。
565 0
升级 .NET 10 不想等 Pomelo.EntityFrameworkCore.MySql?试试 Microting.EntityFrameworkCore.MySql
|
9月前
|
人工智能
从"任天堂仿作"到动态封禁:生成式游戏的内容审核难题
The Verge测评揭示Project Genie在模仿经典游戏风格时的版权困境:关键词过滤易被绕过,输出审核难定“相似度”,人工审核不可持续。问题核心在于AI能否合法模仿创作风格——法律尚未明确,技术尚不成熟。平台需透明规则、分层发布与版权方协作。
432 0
|
3月前
|
人工智能 缓存 前端开发
AI 长篇小说创作管理开源软件:织叙工作台的设定数据与创作工作流设计
织叙工作台是面向长篇创作的开源 Web 平台,以结构化设定、章节编辑、剧情分析和可选向量记忆组织小说生产流程,支持 Docker Compose 与本地部署。
|
3月前
|
人工智能 搜索推荐 测试技术
GEO行业科普:别拿SEO经验忽悠AI时代的新玩家
GEO(生成式引擎优化)是2024年才由普林斯顿大学正式命名的新兴领域,核心是让AI“理解、信任并引用”品牌内容,而非传统SEO的排名与点击。其逻辑迥异:重语义相关性、结构化标记、E-E-A-T可信度与跨平台一致性。2026年市场规模预计达286亿元,但真正落地率不足15%——所谓“十几年GEO经验”,多为SEO的伪服务经验。
|
3月前
|
人工智能 弹性计算 IDE
阿里云热门Agent与大模型活动:云聚AI,从模型到应用,长效权益
本次“云聚AI”主题活动不仅是一次简单的产品促销,更是一场从底层模型能力到上层应用落地的全方位生态展示。活动紧扣“从模型到应用”的核心理念,通过“AI焕新季”新购礼包、百炼基础模型订阅焕新、Qoder CN等热门AI应用的限时特惠,以及精选AI产品组合购等多元化形式,构建了一个覆盖开发者、初创企业及个人创作者的完整服务闭环。无论是需要底层算力支持的模型训练,还是寻求开箱即用的AI编程助手,亦或是希望通过组合购降低上云成本,用户都能在此次活动中找到极具性价比的解决方案,真正实现了从技术尝鲜到生产力转化的无缝衔接。
|
6月前
|
人工智能 安全 网络虚拟化
为什么总显示请求超时或者一直运行中无反应,卸载重装了好几次,把各种安全软件和vpn等都关了也不行
该问题表现为AI工具频繁超时、卡在“生成中”无响应,强制停止无效甚至导致程序卡死。已尝试重装、关闭安全软件及VPN仍无效,疑似网络连接异常、服务端故障或本地环境兼容性问题。
|
6月前
|
自然语言处理 监控 数据挖掘
海外仓客服从"导航菜单人工转接"到"技能组自动分配":跨国物流分层服务体系落地路径
跨境电商物流的客服体系正在经历一场从"人找服务"到"服务找人"的结构性转变。本文基于两个头部物流企业的实践案例,拆解海外仓客服如何从传统的IVR导航菜单人工转接,演进为基于业务属性自动分配的技能组服务体系,并给出分层落地的具体路径。
477 0

热门文章

最新文章