2

I have successfully set up API Versioning in my Core 2.1 API project.

http://localhost:8088/api/Camps/ATL2016/speakers?api-version=x.x

Versions 1.1 and 2.0 work but 1.0 fails with an ambiguity on the Get(string, bool) Actions.

ASP.NET Core Web Server:

MyCodeCamp> fail: Microsoft.AspNetCore.Mvc.Routing.DefaultApiVersionRoutePolicy[1] MyCodeCamp> Request matched multiple actions resulting in ambiguity. Matching actions: MyCodeCamp.Controllers.Speakers2Controller.Get(string, bool) (MyCodeCamp) MyCodeCamp> MyCodeCamp.Controllers.SpeakersController.Get(string, bool) (MyCodeCamp) MyCodeCamp> fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1] MyCodeCamp> An unhandled exception has occurred while executing the request. MyCodeCamp> Microsoft.AspNetCore.Mvc.Internal.AmbiguousActionException: Multiple actions matched. The following actions matched route data and had all constraints satisfied:

Controller Speakers2 is decorated with [ApiVersion("2.0")] so it’s Get(string, bool) action is version 2.0 so why can’t Versioning tell them apart?

Microsoft.AspNetCore.Mvc.Versioning 3.0.0 (can’t install higher due to version conflicts)

Startup.cs:

  services.AddApiVersioning(cfg =>
    { cfg.DefaultApiVersion = new ApiVersion(1, 1);
      cfg.AssumeDefaultVersionWhenUnspecified = true;
      cfg.ReportApiVersions = true;     });

Controllers:

  [Route("api/camps/{moniker}/speakers")]
  [ValidateModel]
  [ApiVersion("1.0")]
  [ApiVersion("1.1")]
  public class SpeakersController : BaseController
  { 
    . . . 
    [HttpGet]
    [MapToApiVersion("1.0")]
    public IActionResult Get(string moniker, bool includeTalks = false)

    [HttpGet]
    [MapToApiVersion("1.1")]
    public virtual IActionResult GetWithCount(string moniker, bool includeTalks = false)

  [Route("api/camps/{moniker}/speakers")]
  [ApiVersion("2.0")]
  public class Speakers2Controller : SpeakersController
  {
    ...
    public override IActionResult GetWithCount(string moniker, bool includeTalks = false)
Joe
  • 4,143
  • 8
  • 37
  • 65

3 Answers3

2

Apparently versioning gets confused with the multiple Getxxx IActionResults.

I got it to work by making the Get action in the Speakers controller virtual and then overriding it in the Speakers2 controller as a placeholder that will not be called. I also had to apply the [ApiVersion("2.0")] only to the GetWithCount action and not the controller.

[Authorize]
[Route("api/camps/{moniker}/speakers")]
[ValidateModel]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
public class SpeakersController : BaseController

  [HttpGet]
  [MapToApiVersion("1.0")]
  [AllowAnonymous]
  public virtual IActionResult Get(string moniker, bool includeTalks = false)



[Route("api/camps/{moniker}/speakers")]
public class Speakers2Controller : SpeakersController

  public override IActionResult Get(string moniker, bool includeTalks = false)
  {  return NotFound(); }

  [ApiVersion("2.0")]
  public override IActionResult GetWithCount(string moniker, bool includeTalks = false)
Joe
  • 4,143
  • 8
  • 37
  • 65
0

The reason that your previous implementation didn't work is because [ApiVersion] and [MapToApiVersion] are not inherited. This might seem counter-intuitive, but if they were, then each subclass would keep accumulating API versions. In the 2nd implementation, you didn't override the original Get so it implicitly became 2.0 as specified by the controller. This is why you were seeing duplicates because it's now ambiguous with GetWithCount.

Chris Martinez
  • 3,185
  • 12
  • 28
0

Multiple candidate actions were found, but none matched the requested service API version '1'. Soln: Use: >[MapToApiVersion("1.0")] attribute on your action method