Search code examples
apicontrollerasp.net-web-api-routingapi-versioningaspnet-api-versioning

Asp.Versioning.Http AmbiguousMatchException: The request matched multiple endpoints on controllers


I am trying to achieve header based versioning on my controllers with Asp.Versioning.Http package version 6.4.0 it is supposed to be super simple here however i get AmbiguousMatchException: The request matched multiple endpoints exception

Here is my Program class

and my controllers defined like that:

    var builder = WebApplication.CreateBuilder(args);
    // Add services to the container.
    builder.Services.AddControllers();

    // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddApiVersioning(options => {
       // options.ApiVersionReader = new HeaderApiVersionReader("api-version");
        options.DefaultApiVersion = new ApiVersion(1.0);
        options.AssumeDefaultVersionWhenUnspecified = true;
        options.ReportApiVersions = true;
    }).EnableApiVersionBinding();
    builder.Services.AddMvc();
    builder.Services.AddSwaggerGen();

    var app = builder.Build();

    // Configure the HTTP request pipeline.
    if (app.Environment.IsDevelopment())
    {
       // app.UseSwagger();
        //app.UseSwaggerUI();
    }

    app.UseHttpsRedirection();
    app.UseRouting();
    app.UseAuthorization();
    app.MapHealthChecks("/health/live");
    app.MapControllers();
    app.Run();
   namespace Things.Service.Controllers.V1
   {
    [ApiController]
    [ApiConventionType(typeof(DefaultApiConventions))]
    [Route("[controller]")]
    [Asp.Versioning.ApiVersion(1.0)]
    public class ThingsController : ControllerBase
    {
       // controller logic
    }
   }
    namespace Things.Service.Controllers.V2
    {
    [ApiController]
    [ApiConventionType(typeof(DefaultApiConventions))]
    [Route("[controller]")]
    [Asp.Versioning.ApiVersion(2.0)]`your text`
    public class ThingsController : ControllerBase
     {
       // controller logic
     }
    }

I get this exception:

    Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple    endpoints. Matches: 

Things.Service.Controllers.V2.ThingsController.GetAllAsync (Things.Service)
Things.Service.Controllers.V1.ThingsController.GetAllAsync (Things.Service)

Solution

  • This is happening because you are missing AddMvc. Don't let the name fool you, this adds MVC Core, not the full MVC stack. Starting in 6.0, the new setup pivots on IApiVersioningBuilder so that all of the setup is in one place and, hopefully, easier to follow. If you're coming from earlier versions (e.g. <= 5.x), this might be a surprise. This change was necessary because AddApiVersioning is now the foundation for Minimal APIs, which doesn't include MVC Core or controller support. AddMvc adds those features.

             
    services
     .AddApiVersioning()     // Asp.Versioning.Http : Core services and Minimal APIs
     .AddMvc()               // Asp.Versioning.Mvc : MVC Core
     .AddApiExplorer()       // Asp.Versioning.Mvc.ApiExplorer : API Explorer
     .AddOData()             // Asp.Versioning.OData : OData support
     .AddODataApiExplorer(); // Asp.Versioning.OData.ApiExplorer : OData API Explorer
    

    Since you're using MVC Core and controllers, you do not need EnableApiVersionBinding. MVC Core has support for Model Binders. AddMvc will register all those services. If you want to receive the incoming ApiVersion in your controller action, you need only add a parameter of type ApiVersion with the name of your choice. For example:

    namespace Things.Service.Controllers.V1
    {
       [ApiVersion(1.0)]
       [ApiController]
       [ApiConventionType(typeof(DefaultApiConventions))]
       [Route("[controller]")]
       public class ThingsController : ControllerBase
       {
          [HttpGet]
          public IActionResult Get(ApiVersion version) => Ok();
       }
    }
    

    Minimal APIs do not have a way to support this type of model binding. EnableApiVersionBinding provides a way to make it work. It won't hurt anything if you've added it, but it's unnecessary.

    Finally, it looks like you have included a version number in your namespaces. If this is indeed your setup, you might consider using the VersionByNamespaceConvention. This would negate the need to decorate controllers with [ApiVersion]. The API version would be derived from the namespace itself. For additional details, see the Version By Namespace Convention documentation.