daxu
daxu

Reputation: 4094

How to set default versioning in ASP.NET Core 6 Web API for my scenario?

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;
        });

Upvotes: 2

Views: 3810

Answers (3)

Krystian
Krystian

Reputation: 13

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

[ApiController]
[Route("api/v1/[controller]")]
[Route("api/[controller]")]
[ApiVersion("1.0")]

Upvotes: 0

Jason
Jason

Reputation: 3960

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));
    });

Upvotes: 0

Argiris Pagidas
Argiris Pagidas

Reputation: 61

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)
    {
    }
}

Upvotes: 1

Related Questions