2

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?

6
  • I think what you want in your Route attribute is v{apiVersion:int} rather than what you currently have, this specifies that the apiVersion parameter for your method is of type int. The supported route constraints are listed in the table here. This assumes that the version parameter in your controller method is of type int and is called apiVersion. Commented May 29, 2019 at 9:47
  • @MattStannett Unfortunately it did not work. The url is the same and I still get multiple match actions. Commented May 29, 2019 at 10:03
  • 1
    Have you decorated your controller with the [ApiController] attribute and your controller methods with the [ApiVersion] attribute as per this documentation. They should look like [ApiVersion("1.0")] and you can go back to using v{version:apiVersion} inside your [Route] attributes, there are example URLs on that page too. Commented May 29, 2019 at 10:09
  • Yes, that is exactly what I did. The controllers are decorated with [ApiController] and [ApiVersion("n.0")] attributes and on a specific method I mapped it as [HttpGet("{id}"), MapToApiVersion("3.0")]. I followed the documentation to the letter but I get multiple actions matched error. Commented May 29, 2019 at 10:15
  • Alternatively you could re-order your route to have [Controller] and value earlier on because I believe it is doing naive matching based on types. So if you had [Route("api/[controller]/v{version:apiVersion}")] on both, or some other item in place of "controller" then the router would be able to tell the difference between the routes, because currently what it is seeing is two routes that look like api/vX and it doesn't know how to pick between them - hence why adding value and controller to the route definition "worked" for you. Hope this makes sense. Commented May 29, 2019 at 10:37

1 Answer 1

6

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.