ASP.NET Web API versioning Migrate from QueryStringApiVersionReader to UrlSegmentApiVersionReader

245 Views Asked by At

I'm currently looking into migrate an API versioning mechanism from using query parameter to use url fragments. However, I need to maintain backwards compatibility, so if the caller specifies the version on the query, it fulfills the new url segment on the route. My routes will go from:

[Route("articles")] to [Route("v{version:apiVersion}/articles")]

However, it seems when passing the api version as query parameter, the routing won't find the route (because the version is missing) and it will return 404.

Is there a way to support both, UrlSegmentApiVersionReader and QueryStringApiVersionReader at the same time?

Thanks

2

There are 2 best solutions below

0
Martin On

You can accomplish this by using ApiVersionReader.Combine

Multiple methods of API versioning can be supported simultaneously. Use the ApiVersionReader.Combine method to compose two or more IApiVersionReader instances together. You can also implement your own method of extracting the requested API version using a custom IApiVersionReader.

See the documentation here

// Example from: https://github.com/dotnet/aspnet-api-versioning/wiki/API-Version-Reader#api-version-reader-composition

.AddApiVersioning(
    options => options.ApiVersionReader = ApiVersionReader.Combine(
        new QueryStringApiVersionReader(),
        new HeaderApiVersionReader() { HeaderNames = { "x-ms-api-version" } } ) );
2
codeninja.sj On

To support both routing options, you need to register the API version in such a way that it supports both.

builder.Services.AddApiVersioning(cfg =>
{
    cfg.DefaultApiVersion = new ApiVersion(2, 0);
    cfg.AssumeDefaultVersionWhenUnspecified = true;
    cfg.ReportApiVersions = true;

    cfg.ApiVersionReader = ApiVersionReader.Combine(
        new QueryStringApiVersionReader("v"),
        new UrlSegmentApiVersionReader()
    );
});

In the controller, you need to specify both routing options at the action/controller level. For instance:

[ApiVersion("1.1")]
[ApiVersion("2.0")]
[Route("v{version:apiVersion}/weather")]
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
    return Enumerable.Range(1, 10).Select(index => new WeatherForecast
    {
        Date = DateTime.Now.AddDays(index),
        TemperatureC = Random.Shared.Next(-20, 55),
        Summary = Summaries[Random.Shared.Next(Summaries.Length)]
    })
    .ToArray();
}

[ApiVersion("1.0")]
[Route("weather")]
[HttpGet]
public IEnumerable<WeatherForecast> GetV1()
{
    return Enumerable.Range(1, 5).Select(index => new WeatherForecast
    {
        Date = DateTime.Now.AddDays(index),
        TemperatureC = Random.Shared.Next(-20, 55),
        Summary = Summaries[Random.Shared.Next(Summaries.Length)]
    })
    .ToArray();
}

P.S. If you intend to route both version requests to the same action method, you need to specify both [Routing] options together on the action method.