如何禁用父控制器路由或允许重复?解决.NET控制器Swagger路由冲突
问题场景
所有控制器继承自基类BaseEntityController,父类定义了不带参数的Get()接口:
public abstract class BaseEntityController : Controller { [HttpGet] [Route("")] public virtual Task<IActionResult> Get() { DoSomeGetAllFunctionallity(); return Task.FromResult<IActionResult>(Ok()); } }
子类ChildController需要实现带查询参数的Get()接口:
public class ChildController : BaseEntityController { [HttpGet] [Route("")] public async Task<IActionResult> Get([FromQuery(Name = "param-1")] int? param1, [FromQuery(Name = "param-2")] bool param2 = false) { DoSomeGetAllFunctionallity(param1, param2); return Ok(); } }
此时用Swagger测试会报错:
Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException: 'Conflicting method/path combination "GET endpoint" for actions - path-to-controller.Get, path-to-controller.Get. Actions require a unique method/path combination for Swagger/OpenAPI 3.0. Use ConflictingActionsResolver as a workaround'
以下是两种可行的解决方案,对应你的期望:
方案1:覆盖父类无参Get并禁用其路由
ASP.NET Core没有[Route(disable: true)]这种用法,正确的做法是用[NonAction]标记覆盖后的父类方法,告诉框架该方法不是接口Action,不会被路由注册和Swagger扫描:
public class ChildController : BaseEntityController { [HttpGet] [Route("")] public async Task<IActionResult> Get([FromQuery(Name = "param-1")] int? param1, [FromQuery(Name = "param-2")] bool param2 = false) { DoSomeGetAllFunctionallity(param1, param2); return Ok(); } // 覆盖父类无参Get,标记为非Action避免冲突 [NonAction] public override Task<IActionResult> Get() { throw new NotImplementedException("请使用带查询参数的Get接口"); } }
这样Swagger只会识别带参数的Get()接口,不会再出现路由冲突。
方案2:让父类无参Get调用子类带参Get(配合Swagger冲突解决)
如果不想禁用父类方法,而是让无参请求自动映射到带参方法(传递默认值),可以在子类中覆盖父类无参Get,同时配置Swagger合并冲突的Action:
第一步:子类中覆盖无参Get并调用带参版本
public class ChildController : BaseEntityController { [HttpGet] [Route("")] public async Task<IActionResult> Get([FromQuery(Name = "param-1")] int? param1, [FromQuery(Name = "param-2")] bool param2 = false) { DoSomeGetAllFunctionallity(param1, param2); return Ok(); } // 覆盖父类无参Get,调用带参版本并传递默认值 public override Task<IActionResult> Get() { return Get(null, false); } }
第二步:配置Swagger冲突解决器
在Program.cs(或Startup.cs)的Swagger配置中添加冲突处理逻辑,让Swagger优先展示带参数的接口:
builder.Services.AddSwaggerGen(c => { // 其他Swagger配置(比如标题、版本等) c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); // 解决路由冲突:选择带参数的Action作为Swagger展示的版本 c.ResolveConflictingActions(apiDescriptions => { return apiDescriptions.OrderByDescending(desc => desc.ParameterDescriptions.Count).First(); }); });
这样既保留了无参请求的兼容性,又能让Swagger正常展示带参数的接口定义,不会报错。
内容的提问来源于stack exchange,提问作者titus

