NSwag自动生成控制器中ModelState校验与400返回方案咨询
当ModelState.IsValid为false时(例如调用接口GET v1/abc/def/{id}时传入gamma,但路径参数id对应的枚举CId仅包含alpha和beta,导致字符串无法映射为合法枚举值),需要返回400 Bad Request。如何在通过NSwag从api.yaml自动生成的控制器中实现该校验?能否修改CController的GetCInfo()方法,或传递ModelState给GetCInfoAsync()?
自动生成的控制器代码(ApiControllers.Generated.cs)
... [GeneratedCode("NSwag", "13.18.2.0 (NJsonSchema v10.8.0.0 (Newtonsoft.Json v13.0.0.0))")] [Route("v1")] public partial class CController : ControllerBase { private ICController _implementation; public CController(ICController implementation) { _implementation = implementation; } ... [HttpGet, Route("abc/def/{id}")] public Task<ActionResult<CInfo>> GetCInfo([BindRequired] CId id, CancellationToken cancellationToken) { return _implementation.GetCInfoAsync(id, cancellationToken); } ... } ... [GeneratedCode("NJsonSchema", "13.18.2.0 (NJsonSchema v10.8.0.0 (Newtonsoft.Json v13.0.0.0))")] public enum CId { [EnumMember(Value = @"alpha")] Alpha= 0, [EnumMember(Value = @"beta")] Beta= 1, } ...
当前配置说明:已在Program.cs中配置System.Text.Json.Serialization的JsonStringEnumConverter,但该配置仅处理JSON格式的参数,对API路由中的参数无效。曾尝试编写中间件校验,但需要逐个API处理字符串,还存在字符串转枚举重复转换的问题,直接校验ModelState.IsValid是更简便的方案。
可行方案分析
1. 在nswag.json中配置添加[ApiController]装饰器
这是最推荐的方案,ASP.NET Core的[ApiController]特性会自动触发模型验证,并在ModelState.IsValid为false时自动返回400 Bad Request,无需手动编写校验代码。
在nswag.json的controllerGeneratorSettings节点中添加addApiControllerAttribute: true配置,生成的控制器会自动带上该特性:
... [ApiController] [GeneratedCode("NSwag", "13.18.2.0 (NJsonSchema v10.8.0.0 (Newtonsoft.Json v13.0.0.0))")] [Route("v1")] public partial class CController : ControllerBase { ... } ...
添加后框架会自动处理路由参数的枚举转换失败问题,直接返回包含验证错误信息的400响应。
2. 在自动生成的控制器中校验ModelState.IsValid
不要直接修改自动生成的文件(重新生成代码后修改会丢失),可以利用部分类特性,在单独的非生成文件中扩展方法:
创建CController.cs文件(不覆盖生成文件):
public partial class CController { public new async Task<ActionResult<CInfo>> GetCInfo([BindRequired] CId id, CancellationToken cancellationToken) { if (!ModelState.IsValid) { return BadRequest(ModelState); } return await _implementation.GetCInfoAsync(id, cancellationToken); } }
自定义方法会覆盖生成的方法,实现手动校验逻辑,且不会被重新生成代码覆盖。
3. 将ModelState传递给实现层
该方案会将控制器层的验证逻辑耦合到业务实现层,不符合分层架构设计原则,不推荐使用。如果确实需要传递,同样通过部分类扩展修改:
在自定义CController.cs文件中:
public partial class CController { public new async Task<ActionResult<CInfo>> GetCInfo([BindRequired] CId id, CancellationToken cancellationToken) { return await _implementation.GetCInfoAsync(id, ModelState, cancellationToken); } }
同时需要修改ICController接口和实现类,添加ModelState参数并处理校验逻辑。
内容的提问来源于stack exchange,提问作者12oClock

