I searched for my problem beforehand in various sources but the answers did not provide me with a solution.
I implemetend Url based web api versioning in a .net core 2.2 project with the way presented here. The version that I used for versioning is the latest Microsoft.AspNetCore.Mvc.Versioning 3.1.2. I also tried to understand how it works from the following sources: source1, source2, source3, source4.
I am having a ValueController with a GET method in a folder called Initial and a Value2Controller in a folder called New. Both folders are subfolders of the 'Controllers' folder.
The structure is as follows:
The routing in ValueController is
[Route("api/v{version:apiVersion}/[controller]")]
and in Value2Controller is:
[Route("api/v{version:apiVersion}/value")]
I have also set options.EnableEndpointRouting = false;
in the Startup.cs and I tried calling api/v1/value or api/v2/value. Both times I get the error: Multiple actions matched. It cannot differentiate between the two controllers actions.
I tried using services.AddApiVersioning();
with no options at all and remove AddVersionedApiExplorer. It does not work. The only thing that works is
putting
[Route("api/v{version:apiVersion}/[controller]")]
in both controllers and make the following api calls:
api/v1/value
and api/v2/value2
.
The configuration in my startup.cs is as follows:
services.AddApiVersioning(options =>
{
options.ReportApiVersions = true;
options.AssumeDefaultVersionWhenUnspecified = true;
options.DefaultApiVersion = new ApiVersion(1, 0);
options.ApiVersionReader = new UrlSegmentApiVersionReader();
options.UseApiBehavior = true;
});
services.AddVersionedApiExplorer(
options =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
options.AssumeDefaultVersionWhenUnspecified = true;
options.DefaultApiVersion = new ApiVersion(1, 0);
});
What am I missing to call either api/v1/value
or api/v2/value
and go to the correct request?
After some more debugging, I finally figured out why it wasn't working, so I am posting the solution to anyone who will face a similar problem. The problem was within the Controller Inheritance.
I had created a CustomBaseController (which I had completely disregarded as problematic for some reason) with some methods for global exception handling, the inheritance goes as follows:
[ApiVersionNeutral]
[Route("api/[controller]")]
[ApiController]
CustomBaseController : Controller
and
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
ValuesController : CustomBaseController { // http method implementations}
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/values")]
[ApiController]
ValuesController : CustomBaseController { // updated http method implementations}
The versioning mechanism did not agree with [ApiVersionNeutral]
attribute even though it made sense to me that the the base controller would not need to change at all. Moreover I only had the basic routing in the base controller.
Thus I got the error with "Multiple actions matched".
I also found out that the version 1 controller, can inherit the routing from the base controller and had no reason to have a routing there. For all subsequent controllers, the routing must be:
[Route("api/v{version:apiVersion}/values")]
.
The working solution along with the initial configuration posted above, is the following:
[Route("api/v{version:ApiVersion}/[controller]")]
[ApiController]
CustomBaseController: Controller {}
[ApiVersion("1.0")]
[ApiController]
ValuesController: CustomBaseController { //code }
[ApiVersion("2.0")]
[ApiController]
[Route("api/v{version:ApiVersion}/values")]
Values2Controller: CustomBaseController { //code }
[ApiVersion("3.0")]
[ApiController]
[Route("api/v{version:ApiVersion}/values")]
Values3Controller: CustomBaseController { //code }
Getting values from the following urls:
api/v1/values
api/v2/values
api/v3/values
Even though my issue was resolved, I still don't understand why [ApiVersionNeutral] would cause the routing to not be able to detect the versions of the other controllers correctly. Any explanation would be highly appreciated. Thank you @Matt Stannett for your comments, they led me to the right direction.