排坑日记: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/10quantity 的值 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。


可选修复方案

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

方案 做法 打破的条件 评估
① 可空类型 intint? 非可空类型 → 不触发隐式 [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 文档生成。

目录
相关文章
|
3天前
|
云安全 人工智能 运维
阿里云SecOps Agent,全新安全跨产品执行体验
自然语言驱动 云安全中心/WAF/CFW/ 等多款安全产品联动
1593 2
|
3天前
|
机器学习/深度学习 人工智能 调度
🐴 HappyHorse 1.1 现已上线阿里云百炼!快来查收模型使用指南,现在调用享 6 折~
HappyHorse 1.1 是新一代视频生成大模型,全面升级动态表现力、角色一致性、指令遵循、视觉质感与音画协同能力。支持I2V/T2V/R2V三类生成,适配短剧、电商广告、品牌营销等场景,提供高质、流畅、可控的AI视频生产力。
557 3
🐴 HappyHorse 1.1 现已上线阿里云百炼!快来查收模型使用指南,现在调用享 6 折~
|
14天前
|
缓存 测试技术 API
Qwen 3.7 Plus 与 Max 实测:性价比与多模态能力差异解析(2026)
2026 年 6 月 1 日,阿里悄无声息地发布了 Qwen 3.7 Plus,距 Qwen 3.7 Max 上线刚好 11 天。同样的 1M 上下文,同样的 35 小时自治上限。但价格才是头条:Plus 是 0.40/M输入,Max是 2.50/M——便宜约 6 倍——并且还能看图、看视频。Vision Arena 上 Plus 已经排到 #16。所以这周真正值得讨论的问题不是”要不要为视觉能力买单”,而是”Max 凭什么用 6 倍价格换来 2 个百分点的 benchmark 领先”。
|
15天前
|
JavaScript 定位技术 API
CodeGraph 爆火:编程 Agent 需要的不是更多上下文,而是一张提前画好的代码地图
CodeGraph 是一款爆火的本地代码智能工具,通过 tree-sitter 解析 AST 构建结构化知识图谱(存于 SQLite),为编程 Agent 提前生成“代码地图”。它显著降低 Agent 在中大型项目中的探索成本——实测工具调用减少71%、Token 降57%、速度提升46%,支持19+语言及主流框架路由识别,完全离线、无需 API Key。
905 11
CodeGraph 爆火:编程 Agent 需要的不是更多上下文,而是一张提前画好的代码地图
|
2天前
|
人工智能 监控 前端开发
Electron 监控:让桌面 Agent 监控触手可及
一行代码实现Electron桌面端全景监控,自动还原崩溃现场、预警内存泄漏、全链路追踪、 SSE流式响应与交互埋点,让 AI 助手运行状态清晰可见,助力快速恢复稳定与流畅。
178 125
|
2天前
|
消息中间件 人工智能 Kafka
AI 时代,实时入湖正在告别 ETL:从 Kafka 到 Iceberg 的架构减法
本文围绕“零 ETL”这一趋势,讨论流数据入湖为什么需要做架构减法,并结合 Kafka × Table Bucket 的实践,分析一种将通用入湖能力前移到消息与表存储链路中的方案,如何在降低复杂度的同时,兼顾实时性、一致性、Schema 演进、CDC 语义与开放生态兼容。
184 121
|
7天前
|
缓存 人工智能 运维
GLM 5.2自托管全流程实战:硬件选型、vLLM/SGLang部署与成本盈亏测算
2026年智谱发布GLM 5.2超大混合专家模型,区别于以往仅开放API的闭源大模型,该模型权重以MIT开源协议对外发布,企业与开发者可完整下载、本地审计、私有化部署,实现数据不出环境、自定义微调、自主调度推理资源。GLM 5.2拥有753B总参数,原生支持百万级上下文窗口,在代码生成、长文档推理、数学逻辑等多项基准测试中对标国际顶尖商用模型,是首款可完整自托管的前沿代码向大模型。
614 0
|
15天前
|
人工智能 运维 JavaScript
阿里云Qoder CN(原通义灵码)全解析 产品形态、版本划分与技术适配说明
在AI辅助开发与智能办公工具持续普及的当下,阿里云旗下原通义灵码正式更名为Qoder CN,同时延伸出QoderWork CN、Qoder CN CLI、Qoder CN Mobile等多款配套产品,形成覆盖代码开发、日常办公、终端交互、移动端使用的完整工具矩阵。Qoder CN核心定位为AI智能编码助手,深度适配主流代码编辑器、集成开发环境以及终端场景;QoderWork CN则偏向桌面端综合办公辅助,二者面向不同使用场景,划分了多个版本档位,搭配差异化资源配额、功能权限与计费规则,同时兼容多款主流大模型。
975 8