0

After getting Api versioning fixed with following in .net core

            services.AddApiVersioning(options =>
            {
                options.AssumeDefaultVersionWhenUnspecified = true;
                options.DefaultApiVersion = new ApiVersion(1, 0);
                options.UseApiBehavior = false; //If true [ApiController] attribute required for versioning
            })
            .AddVersionedApiExplorer(options =>
            {
                options.GroupNameFormat = "'v'VVV";
                options.SubstituteApiVersionInUrl = true;
            });

next problem I faced was having default api version show up swagger document. As shown below image. enter image description here

There are lot of articles to fix it using swashbuckle but how to get it fixed with NSwag?

Anayag
  • 145
  • 1
  • 10

2 Answers2

2

With NSwug we can use a DocumentProcessors which can be used to filter out the unwanted swagger paths.

I have used below nuget packages

<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.0.0" /> 
<PackageReference Include="NSwag.AspNetCore" Version="13.9.4" />

Below shows how I registered them and fixed the problem

        public void ConfigureServices(IServiceCollection services)
    {
            ....
            
            services.AddApiVersioning(options =>
            {
                options.AssumeDefaultVersionWhenUnspecified = true;
                options.DefaultApiVersion = new ApiVersion(1, 0);
            })
            .AddVersionedApiExplorer(options =>
            {
                options.GroupNameFormat = "'v'VVV";
                options.SubstituteApiVersionInUrl = true;
            });
            
            services.AddSwaggerDocument(config =>
            {
                SwaggerConfigure(config, "v1", true);
            });

            services.AddSwaggerDocument(config =>
            {
                SwaggerConfigure(config, "v2", false);
            });
    }

SwaggerConfigure implementation

        private void SwaggerConfigure(AspNetCoreOpenApiDocumentGeneratorSettings configure, string version, bool isDefaultVersion)
    {
        configure.DocumentName = version;
        configure.ApiGroupNames = new[] { version };

        if (isDefaultVersion)
        {
            configure.DocumentProcessors.Add(new RemoveVersionFromDefault(version));
        }

        configure.PostProcess = document =>
        {
            document.Info.Version = version;
            document.Info.Title = $"{GetApiTitle()} API";
        };

        configure.AllowNullableBodyParameters = false;
        configure.SerializerSettings = new JsonSerializerSettings();
    }

DocumentProcessor implementation

    public class RemoveVersionFromDefault : IDocumentProcessor
    {
    private readonly string _defaultVersion;

    public RemoveVersionFromDefault(string defaultVersion)
    {
        _defaultVersion = defaultVersion;
    }

    public void Process(DocumentProcessorContext context)
    {
        var keys = new List<string>();
        foreach (var (key, value) in context.Document.Paths)
        {
            if (key.Contains($"/{_defaultVersion}/"))
            {
                keys.Add(key);
            }
        }

        foreach (var key in keys)
        {
            context.Document.Paths.Remove(key);
        }
    }
}

Thats it. Hope this will help someone who is looking for a similar solution with NSwug

Anayag
  • 145
  • 1
  • 10
0

There are several ways that this scenario can be solved. An alternative approach would be to create and register a custom IApiDescriptionProvider that is executed last. From the resultant ApiDescription instances provided to the context of IApiDescriptionProvider.OnProvidersExecuted you can then find what you're looking for and remove the descriptors from the results. In this scenario, you're looking to remove the descriptors that match the default API version with the API version parameter.

This approach has several benefits:

  1. You can get the configured default API version by injecting IOptions<ApiVersioningOptions>
  2. You can match the API version using the ApiDescriptor.GetApiVersion extension method and filter descriptors that still have the API version parameter. You can hardcode the name or get it from the injected ApiVersioningOptions.RouteConstraintName in step 1. No magic string parsing or matching.
  3. If you switch between Open API (Swagger) document generators (e.g. NSwag to Swashbuckle or vice versa) no other code changes are required to address this scenario

Aside: The comment regarding:

options.UseApiBehavior = false; //If true [ApiController] attribute required for versioning

isn't quite accurate. You're probably getting the results that you want, but there's more to the story. This behavior and customization isn't well-documented and hardly anyone changes it. The behavior is really controlled by IApiControllerSpecification and IApiControllerFilter. The default filter implementation just runs through all registered specifications so there is little reason to ever change it. Each specification defines whether a controller matches. There is an out-of-the-box specification that matches [ApiController], but that isn't the only specification that exists (ex: OData controllers). The reason the UseApiBehavior option exists at all is because before [ApiController] (in 2.1 I think), there was no clear way to disambiguate between UI-only and API-only controllers. That really irked some developers who mixed both types of controllers. Conversely, flipping a hard switch to disable that potentially broke developers who wanted that behavior. Setting the value to false merely ignores all filtering and reverts back to the days before [ApiController] which was introduced in a feature enhancement called API Behaviors. The new design allows you to replace or create new specifications should you decide to do so. It may not apply to you, but it should be known that the configuration option is not a hard on or off for matching [ApiController] and other, more complex, controller filtering is possible.

Chris Martinez
  • 3,185
  • 12
  • 28
  • Thanks @Chris for the input and comment. Really gives a lot of insight. – Anayag Mar 25 '21 at 09:17
  • I found out that options.UseApiBehavior = false; affects the actual swagger document as it actually removed all api behaviours. Instead I forced the apicontroller in assembly level as [assembly:ApiController] as mentioned https://www.strathweb.com/2019/01/enabling-apicontroller-globally-in-asp-net-core-2-2/ – Anayag Mar 25 '21 at 16:45