MichielDeRouter
MichielDeRouter

Reputation: 426

Why does Swagger need a version requestparameter, when the API version is in the URL?

I have implemented Swagger using Swashbuckle and MultipleApiVersions and it works like a charm. But I find it a bit ugly that the current setup requires a api-version request parameter. I assumed the version could be determined by the url /api/V1/Test.

How do I remove the api-version parameter and instruct swagger to base the version on the URL?

enter image description here

private static void SetupApiVersioningAndSwagger(IAppBuilder builder, AutofacWebApiDependencyResolver resolver)
        {
            // we only need to change the default constraint resolver for services that want urls with versioning like: ~/v{version}/{controller}
            var constraintResolver = new DefaultInlineConstraintResolver() { ConstraintMap = { ["apiVersion"] = typeof(ApiVersionRouteConstraint) } };
            var configuration = new HttpConfiguration();
            configuration.DependencyResolver = resolver;
            var httpServer = new HttpServer(configuration);

            // reporting api versions will return the headers "api-supported-versions" and "api-deprecated-versions"
            configuration.AddApiVersioning(o =>
            {
                o.ReportApiVersions = true;
                o.DefaultApiVersion = new ApiVersion(1, 0);
                o.AssumeDefaultVersionWhenUnspecified = true;
            });
            configuration.MapHttpAttributeRoutes(constraintResolver);

            // add the versioned IApiExplorer and capture the strongly-typed implementation (e.g. VersionedApiExplorer vs IApiExplorer)
            // note: the specified format code will format the version as "'v'major[.minor][-status]"
            var apiExplorer = configuration.AddVersionedApiExplorer(
                options =>
                {
                    options.GroupNameFormat = "'v'VVV";

                    // note: this option is only necessary when versioning by url segment. the SubstitutionFormat
                    // can also be used to control the format of the API version in route templates
                    options.SubstituteApiVersionInUrl = true;
                });

            configuration.EnableSwagger(
                             "{apiVersion}/swagger",
                             swagger =>
                             {
                                 // build a swagger document and endpoint for each discovered API version
                                 swagger.MultipleApiVersions(
                                     (apiDescription, version) => apiDescription.GetGroupName() == version,
                                     info =>
                                     {
                                         foreach (var group in apiExplorer.ApiDescriptions)
                                         {
                                             var description = string.Empty;

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

                                             info.Version(@group.Name, $"Force Search API v{@group.ApiVersion}")
                                                 .Description(description);
                                         }
                                     });

                                 swagger.UseFullTypeNameInSchemaIds();
                             })
                         .EnableSwaggerUi(swagger => swagger.EnableDiscoveryUrlSelector());

            builder.UseWebApi(httpServer);
        }

Upvotes: 1

Views: 3748

Answers (1)

Chris Martinez
Chris Martinez

Reputation: 4418

The reason this is happening is because the default IApiVersionReader is a composition of both the query string and URL segment methods. This allows you to use either approach without any additional configuration. The reader implementations also describe where and how an API version is consumed so that it can be reported as a parameter. Since there are 2 readers configured at different locations, the result is 2 parameters. This is generally not a problem until you integrate OpenAPI/Swagger.

The default configuration looks like this:

configuration.AddApiVersioning(
    options => options.ApiVersionReader = ApiVersionReader.Combine(
        new QueryStringApiVersionReader(),
        new UrlSegmentApiVersionReader()));

Solution

Update your configuration as follows:

configuration.AddApiVersioning(
    options =>
    {
        options.ReportApiVersions = true;
        options.DefaultApiVersion = new ApiVersion(1, 0); // NOTE: already the default
        options.ApiVersionReader = new UrlSegmentApiVersionReader();
        options.AssumeDefaultVersionWhenUnspecified = true;
    });

Afterward, there will be only a single parameter. Since you've configured options.SubstituteApiVersionInUrl = true, the net result will be zero API version parameters because the value is baked directly into the generated API description URL.

Upvotes: 2

Related Questions