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