Skip to content

Asp Net Core + OData + Swagger + Url Segment Versioning #365

Closed
@SuricateCan

Description

@SuricateCan

Hi all,

I'm having trouble setting up versioning by URL segment. After figuring out how to add the version to the route template, I'm faced with these situations:

  1. The routing works as expected. Each call is directed to the correct version of the api;
  2. Swagger does not recognize the template, and instead of registering routes like this api/v1/values it is showing like this api/v{version:apiVersion}/values which, as expected, causes a lot of troubles;
  3. And finally, when trying to show the document for another version, I get and exception saying my routes are not unique.

Here is how I've setup the application:

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc()
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    services.AddApiVersioning(options =>
    {
        options.ReportApiVersions = true;
        options.ApiVersionReader = new UrlSegmentApiVersionReader();
    });
    services.AddOData().EnableApiVersioning();
    services.AddODataApiExplorer(
        options =>
        {
            options.GroupNameFormat = "'v'VVV";
            options.SubstituteApiVersionInUrl = true;
        });

    services.AddSwaggerGen(a =>
    {
        var provider = services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>();

        foreach (var description in provider.ApiVersionDescriptions)
        {
            var info = new Info
            {
                Title = "My API Title",
                Version = description.ApiVersion.ToString(),
                Description = "My API Description"
            };

            if (description.IsDeprecated)
                info.Description += " NOTE: This API has been deprecated";

            a.SwaggerDoc(description.GroupName, info);
        }

        a.ParameterFilter<SwaggerDefaultValues>();

        var filePath = Path.Combine(PlatformServices.Default.Application.ApplicationBasePath, $"{PlatformServices.Default.Application.ApplicationName}.xml");
        a.IncludeXmlComments(filePath);

        a.DescribeAllEnumsAsStrings();
    });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env, VersionedODataModelBuilder versionedModelBuilder, IApiVersionDescriptionProvider versionDescriptionProvider)
{
    app.UseMvc(routeBuilder =>
    {
        routeBuilder.Select().Expand().Filter().OrderBy().Count();
        routeBuilder.MapVersionedODataRoutes("odata", "api/v{v:apiVersion}", versionedModelBuilder.GetEdmModels());
    });
    app.UseSwagger();
    app.UseSwaggerUI(a =>
    {
        // build a swagger endpoint for each discovered API version
        foreach (var description in versionDescriptionProvider.ApiVersionDescriptions)
        {
            a.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName);
        }
    });
}

SwaggerDefaultValues.cs

public class SwaggerDefaultValues : IParameterFilter
{
    public void Apply(IParameter parameter, ParameterFilterContext context)
    {
        if (!(parameter is NonBodyParameter nonBodyParameter))
            return;

        if (nonBodyParameter.Description == null)
        {
            nonBodyParameter.Description = context.ApiParameterDescription.ModelMetadata?.Description;
        }

        if (context.ApiParameterDescription.RouteInfo == null)
            return;

        if (nonBodyParameter.Default == null)
        {
            nonBodyParameter.Default = context.ApiParameterDescription.RouteInfo.DefaultValue;
        }

        parameter.Required |= !context.ApiParameterDescription.RouteInfo.IsOptional;
    }
}

So, am I doing something wrong? Is this scenario supported?

Thanks in advance.

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions