Search code examples
c#asp.net-web-apiasp.net-coreapi-versioning

Core 2.1 APIVersioning Action ambiguity


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)

Solution

  • 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)