Search code examples
c#asp.net-coreswagger-uiwebapi

how to set version in url swaggerUI - .NET 6


I have just updated the api versioning libraries.

before I had:

  • Microsoft.AspNetCore.Mvc.Versioning (5.0.0)
  • Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer (5.0.0)

now I have:

  • Asp.Versioning.Mvc (6.4.0)
  • Asp.Versioning.Mvc.ApiExplorer(6.4.0)

before this update the version was written automatically but now it requires me to pass it as a parameter.

enter image description here

Controller:

[Route("v{version:apiVersion}/Sales")]
public class SalesController : CustomController
{
    private readonly ISalesBusinessLogic _SalesBusinessLogic;
    private readonly IValidator<getSalesRequestDto> _getSalesRequestDto;
    public SalesController(
        ISalesBusinessLogic SalesBusinessLogic,
        IValidator<getSalesRequestDto> getSalesRequestDto
        )
    {
        _SalesBusinessLogic = SalesBusinessLogic;
        _getSalesRequestDto = getSalesRequestDto;
    }

    [MapToApiVersion("4.0")]
    [HttpGet]
    [SwaggerResponseExample(StatusCodes.Status200OK, typeof(SalesResponseViewModelExample))]
    [SwaggerResponse(StatusCodes.Status200OK, Type = typeof(GlobalResponse<SalesResponseViewModel>))]
    public async Task<IActionResult> getSales([FromQuery] getSalesRequestDto request)
    {
        var results = await _getSalesRequestDto.ValidateAsync(request);
        results.AddToModelState(ModelState, null);
        if (!results.IsValid)
        {
            return new ValidationFailedResult(results);
        }
        var result = await _SalesBusinessLogic.getSales(request);
        return Ok(result);
    }
}

CustomController:

[ApiController]
[EnableCors("cors")]
[Authorize]
[ApiVersion("4.0")]
[Produces(MediaTypeNames.Application.Json)]
[SwaggerResponseExample(StatusCodes.Status500InternalServerError, typeof(GlobalResponseErrorExample))]
[SwaggerResponse(StatusCodes.Status500InternalServerError, Type = typeof(GlobalResponse<object>))]
[SwaggerResponseExample(StatusCodes.Status401Unauthorized, typeof(GlobalResponseInvalidTokenExample))]
[SwaggerResponse(StatusCodes.Status401Unauthorized, Type = typeof(GlobalResponse<object>))]
public class CustomController : ControllerBase
{
}

ConfigureSwaggerOptions:

public class ConfigureSwaggerOptions : IConfigureNamedOptions<SwaggerGenOptions>
{
    private readonly IApiVersionDescriptionProvider provider;

    public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider)
    {
        this.provider = provider;
    }

    public void Configure(SwaggerGenOptions options)
    {
        // add swagger document for every API version discovered
        foreach (var description in provider.ApiVersionDescriptions)
        {
            options.SwaggerDoc(
                description.GroupName,
                CreateVersionInfo(description));

            options.ExampleFilters();

            options.AddSecurityDefinition(
                description.GroupName,
                CreateSecurityScheme());

            options.AddSecurityRequirement(CreateSecurityRequirement(description.GroupName));
        }
    }

    public void Configure(string name, SwaggerGenOptions options)
    {
        Configure(options);
    }

    private OpenApiInfo CreateVersionInfo(ApiVersionDescription description)
    {
        var info = new OpenApiInfo()
        {
            Title = "API eCommerce",
            Version = description.ApiVersion.ToString()
        };

        if (description.IsDeprecated)
        {
            info.Description += " This API version has been deprecated.";
        }

        return info;
    }

    private OpenApiSecurityScheme CreateSecurityScheme()
    {
        var securityScheme = new OpenApiSecurityScheme()
        {
            Name = "Authorization",
            Type = SecuritySchemeType.ApiKey,
            Scheme = "Bearer",
            BearerFormat = "JWT",
            In = ParameterLocation.Header,
            Description = "JWT Authorization header using the Bearer scheme. \r\n\r\n Enter 'Bearer' [space] and then your token in the text input below.\r\n\r\nExample: \"Bearer token\"",
        };
        return securityScheme;
    }

    private OpenApiSecurityRequirement CreateSecurityRequirement(string groupName)
    {
        var securityRequirement = new OpenApiSecurityRequirement()
        {
            {
                new OpenApiSecurityScheme{
                Reference = new OpenApiReference
                {Type = ReferenceType.SecurityScheme, Id = groupName}
                }, new string[] {}
            }
        };

        return securityRequirement;
    }
}

Program.cs:

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.ConfigureOptions<ConfigureSwaggerOptions>();

//API Versioning
builder.Services.AddApiVersioning(options =>
{
    options.AssumeDefaultVersionWhenUnspecified = true;
    options.DefaultApiVersion = new ApiVersion(4, 0);
    options.ReportApiVersions = true;
}).AddApiExplorer(setup =>
{
    setup.GroupNameFormat = "'v'VVV";
    setup.SubstituteApiVersionInUrl = true;
}).AddMvc();

var app = builder.Build();
var provider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(options =>
    {
        foreach (var description in provider.ApiVersionDescriptions)
        {
            options.SwaggerEndpoint($"{description.GroupName}/swagger.json",
            description.GroupName.ToUpperInvariant());
        }
    });
}

what am I missing to configure?


Solution

  • I managed to find the solution using this line:

    options.Conventions.Add(new VersionByNamespaceConvention())
    

    would look like this in program.cs:

    builder.Services.AddApiVersioning(options =>
    {
        options.AssumeDefaultVersionWhenUnspecified = true;
        options.DefaultApiVersion = new ApiVersion(4, 0);
        options.ReportApiVersions = true;
    }).AddApiExplorer(setup =>
    {
        setup.GroupNameFormat = "'v'VVV";
        setup.SubstituteApiVersionInUrl = true;
    }).AddMvc(
    options =>
    {
        // automatically applies an api version based on the name of
        // the defining controller's namespace
        options.Conventions.Add(new VersionByNamespaceConvention());
    });