1

Just realised that my understanding about ASP.NET Core 6 Web API versioning is wrong.

This is my controller:

[ApiVersion("1.0")]
[ApiController]
[Authorize]
public class FundController 
{
    [MapToApiVersion("1.0")]
    [Route("/Fund/v{version:apiVersion}/delta")]
    public async Task<List<PortfolioHolding<Holding>>> Delta([FromQuery] Request dataModel)
    {
    }
}

What I want is to support route /Fund/v1.0/delta and /Fund/delta, when versioning not provided by the consumer (e.g. calling /Fund/delta), the default version will be hit.

So I configured the versioning like this. However, when I call /Fund/delta, I get a http 404 error.

But /Fund/v1.0/delta will hit the correct controller.

What am I doing wrong?

services.AddApiVersioning(option =>
        {
            option.DefaultApiVersion = new ApiVersion(1, 0);
            option.AssumeDefaultVersionWhenUnspecified = true;
            option.ReportApiVersions = true;
        });
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
daxu
  • 3,514
  • 5
  • 38
  • 76
  • Do you want version control like this? https://stackoverflow.com/questions/56738937/net-core-webapi-fall-back-api-version-in-case-of-missing-minor-version – Tupac Feb 03 '22 at 08:07

3 Answers3

1

The problem is that you have not specified the routes in the controller.

You should add the default route as well as the formatted version route. Then you should ensure that your endpoints have the version specified in the MapToApiVersion attribute.

Here is a code sample of what your controller should look like:

[ApiVersion("1.0")]
[ApiVersion("2.0")]
[Route("[controller]")]
[Route("[controller]/v{version:apiVersion}")]
public class FundController : ControllerBase
{
    [MapToApiVersion("1.0")]
    [Route("delta")]
    [HttpGet]
    public async Task<List<PortfolioHolding<Holding>>> DeltaV1([FromQuery] Request dataModel)
    {
    }

    [MapToApiVersion("2.0")]
    [Route("delta")]
    [HttpGet]
    public async Task<List<PortfolioHolding<Holding>>> DeltaV2([FromQuery] 
Request dataModel)
    {
    }
}
0

Usually, it's pretty easy to do this that way. The disadvantage of this approach is that you need to manually change the "default" version of API with this attribute

enter image description here

Krystian
  • 13
  • 2
  • 2
    Please don't post code as images. Code should be pasted as text, and formatted using Markdown. (The toolbar in the Stack Overflow editor can help you with this.) Code within images is harder to read, less accessible, can't be copied, and doesn't show up in relevant searches. Please [edit] your post to include the code as text; this will help you get better responses, and help prevent your answer from getting deleted. – Jeremy Caney Aug 04 '22 at 02:18
0

Another way to do this without having [Route("api/[controller")] on every controller is to implement and use IControllerConvention from Asp.Versioning.Conventions

internal class DefaultRouteHandler : IControllerConvention
{
    public bool Apply(IControllerConventionBuilder builder, ControllerModel controller)
    {
        var attributeModel = new AttributeRouteModel();
        var selectorModel = new SelectorModel();

        attributeModel.Template = "api/[controller]";
        selectorModel.AttributeRouteModel = attributeModel;
        controller.Selectors.Add(selectorModel);

        return true;
    }
}

Then in your main...

services.AddApiVersioning(option =>
        {
            option.DefaultApiVersion = new ApiVersion(1, 0);
            option.AssumeDefaultVersionWhenUnspecified = true;
            option.ReportApiVersions = true;
        })
        .AddMvc(option => option.Conventions.Add(new DefaultRouteHandler()));

To prevent Swagger from showing duplicate (versioned + default) routes, you can do something like

builder.Services.AddSwaggerGen(x =>
    {
        x.DocInclusionPredicate((v, d) => d.RelativePath.Contains($"/{v}/", StringComparison.OrdinalIgnoreCase));
    });
Jason
  • 3,844
  • 1
  • 21
  • 40