ASP.NET Core中替代将SuppressAsyncSuffixInActionNames设为false的正确做法
ASP.NET Core中CreatedAtAction配合Async后缀Action的官方推荐解决方案
问题背景
我正在学习ASP.NET Core相关教程,其中在POST方法中使用CreatedAtAction()实现数据库对象创建的逻辑,示例代码如下:
public async Task<ActionResult<ItemDto>> PostAsync(CreateItemDto createItemDto) { var item = new Item { ... }; await itemsRepository.CreateAsync(item); return CreatedAtAction(nameof(GetByIdAsync), new { id = item.Id }, item); }
这段代码的作用是在响应体中返回已创建的对象,同时在响应头中添加location头,响应头示例如下:
content-type: application/json; charset=utf-8 date: Wed02 Jun 2021 15:21:13 GMT location: https://localhost:5001/items/10189cc2-bb99-4315-9247-a6990bc9563a server: Kestrel
ASP.NET Core 3.0存在一项破坏性变更:作为历史bug的修复内容,ASP.NET Core MVC默认会裁剪Action名称的Async后缀,该变更会影响路由与链接生成逻辑。如果不做特殊配置,调用CreatedAtAction()时会抛出如下异常:
fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1] An unhandled exception has occurred while executing the request. System.InvalidOperationException: No route matches the supplied values. at Microsoft.AspNetCore.Mvc.CreatedAtActionResult.OnFormatting(ActionContext context)
很多教程给出的解决方案是将SuppressAsyncSuffixInActionNames设为false恢复旧行为,配置代码如下:
public void ConfigureServices(IServiceCollection services) { services.AddControllers(options => { options.SuppressAsyncSuffixInActionNames = false; }); ... }
但该方案相当于恢复了原有路由bug,另外两种常见 workaround 要么直接硬编码URL,要么通过Replace方法去掉nameof返回值中的Async后缀,都不够优雅。
官方推荐解决方案
- 方案1:使用
[ActionName]特性显式标注Action名称(最优方案)
这是微软官方文档明确推荐的处理方式,直接在带Async后缀的Action方法上添加特性固定名称,既保留了.NET异步方法的命名规范,又不会受默认裁剪规则影响,也不需要修改全局配置:
[ActionName(nameof(GetByIdAsync))] public async Task<ActionResult<ItemDto>> GetByIdAsync(Guid id) { // 业务逻辑实现 }
配置完成后,原有CreatedAtAction(nameof(GetByIdAsync), new { id = item.Id }, item)代码即可正常匹配路由,不需要额外调整。
- 方案2:调用
CreatedAtAction时直接使用裁剪后的名称
如果你不想修改Action方法的特性定义,也可以直接传入去掉Async后缀的Action名称,不需要调整其他配置:
return CreatedAtAction("GetById", new { id = item.Id }, item);
内容的提问来源于stack exchange,提问作者Jeff Dege
相关产品推荐
相关产品推荐

