I'm trying to embed Swagger in my Asp Core (.Net 6) project where there are some cases of route overriding. However, the issue I'm facing can be reproduced even on the following case.
Consider a minimal Asp Core (.Net 6) app. For instance, just like the one used by Swashbuckle as test: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/master/test/WebSites/MinimalApp
Now, consider two controllers in the same app:
[ApiController]
[Route("/api")]
public class MyFallbackController : ControllerBase
{
[HttpGet("values", Order = 1)]
public ActionResult<object> GetValues()
{
return new[] { 1, 2, 3 };
}
}
[ApiController]
[Route("/api")]
public class MyOverrideController : ControllerBase
{
[HttpGet("values", Order = 0)]
public ActionResult<object> GetValues()
{
return new[] { 4, 5, 6 };
}
}
Notice that the routes are exactly the same, but only the first (Order = 0
) will be considered.
If I run the app and navigate to:
https://localhost:7263/api/values
the response gives the expected result: [4, 5, 6]
However, when I try to access the Swagger section, it does not work because (apparently) it figures as a collision the controller pair:
An unhandled exception occurred while processing the request.
SwaggerGeneratorException: Conflicting method/path combination "GET api/values" for actions - WebApplication2.MyFallbackController.GetValues (WebApplication2),WebApplication2.MyOverrideController.GetValues (WebApplication2). Actions require a unique method/path combination for Swagger/OpenAPI 3.0. Use ConflictingActionsResolver as a workaround
Is there any way to get rid of that problem?
CodePudding user response:
Found it.
The trick is in the SwaggerGen
configuration, as the exception message suggests, by the way.
using Microsoft.AspNetCore.Mvc.ApiExplorer;
services.AddSwaggerGen(c =>
{
c.ResolveConflictingActions(apiDescriptions =>
{
int best_order = int.MaxValue;
ApiDescription? best_descr = null;
foreach (var curr_descr in apiDescriptions)
{
int curr_order = curr_descr.ActionDescriptor.AttributeRouteInfo?.Order ?? 0;
if (curr_order < best_order)
{
best_descr = curr_descr;
}
}
return best_descr;
});
});
Basically, the above function selects only the ApiDescription
with the lowest order among the duplicates.
That is my naive yet effective solution. For instance, I don't know if the input collection is given already sorted by order. In that case, the code could be even simpler.