Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSwag multiple document endpoint

Tags:

nswag

It's possible to have multiple document endpoint like in swashbuckle?

options.SwaggerEndpoint("/swagger/v1/swagger.json", "API v1");
options.SwaggerEndpoint("/swagger/v2/swagger.json", "API v2");

If Yes, how to decorate api calls so that some belongs to one versione and some to the other version?

So According to Rico Suter suggestion what I've done it's kinda like that:

ApiVersionAttribute.cs

public class ApiVersionAttribute:Attribute
    {
        private List<string> _versions = new List<string>();

        public List<string> Versions { get { return _versions; } }

        public ApiVersionAttribute(string version) {
            Versions.Add(version);
        }
    }

MyApiVersionProcessor.cs

        public string Version { get; set; }

        public MyApiVersionProcessor(string version)
        {
            this.Version = version;
        }

        public new Task<bool> ProcessAsync(OperationProcessorContext context)
        {
            bool returnValue = true;

            var versionAttributes = context.MethodInfo.GetCustomAttributes()
                .Concat(context.MethodInfo.DeclaringType.GetTypeInfo().GetCustomAttributes())
                .Where(a => a.GetType()
                                .IsAssignableTo("MapToApiVersionAttribute", TypeNameStyle.Name) 
                            || a.GetType()
                                .IsAssignableTo("ApiVersionAttribute", TypeNameStyle.Name)
                       )
                .Select(a => (dynamic)a)
                .ToArray();

            var versionAttribute = versionAttributes.FirstOrDefault();

            if (null == versionAttribute)
            {
                returnValue = false;
            }
            else
            {
                if (ObjectExtensions.HasProperty(versionAttribute, "Versions")
                    && Version.Equals(versionAttribute.Versions[0].ToString()))
                {
                    ReplaceApiVersionInPath(context.OperationDescription, versionAttribute.Versions);
                }
                else {
                    returnValue = false;
                }
            }

            return Task.FromResult(returnValue);
        }

        private void ReplaceApiVersionInPath(SwaggerOperationDescription operationDescription,
            dynamic versions)
        {
            operationDescription.Path = operationDescription.Path.Replace("{version:apiVersion}",
                versions[0].ToString());
        }
    }

And in my Global.asax

                // NSwag
                // https://github.com/RSuter/NSwag/wiki/OwinGlobalAsax#integration                
                app.UseSwaggerUi(typeof(WebApiApplication).Assembly, new SwaggerUiSettings
                {                       
                    //TypeNameGenerator = new MyTypeNameGenerator(),
                    MiddlewareBasePath = "/swagger",
                    SwaggerRoute = "/swagger/v1/swagger.json",
                    Version = "1.0.0.0",
                    // https://github.com/RSuter/NSwag/wiki/Middlewares
                    OperationProcessors =
                    {
                        new MyApiVersionProcessor("v1")
                    },
                    PostProcess = document =>
                    {                        
                        document.BasePath = "/";
                        document.Produces
                            = new List<string> { "application/json"
                                                , "text/json"
                                                , "text/html"
                                                , "plain/text"
                                                , "application/xml"};
                        document.Consumes
                            = document.Produces;
                        document.Info.Title = "Document V1";                     
                    }

                });

                app.UseSwaggerUi(typeof(WebApiApplication).Assembly, new SwaggerUiSettings
                {
                    //TypeNameGenerator = new MyTypeNameGenerator(),
                    MiddlewareBasePath = "/swagger",
                    SwaggerRoute = "/swagger/v2/swagger.json",
                    Version = "2.0.0.0",
                    OperationProcessors =
                    {
                        new MyApiVersionProcessor("v2")
                    },
                    PostProcess = document =>
                    {
                        document.BasePath = "/";
                        document.Produces
                            = new List<string> { "application/json"
                                                , "text/json"
                                                , "text/html"};
                        document.Consumes
                            = document.Produces;
                        document.Info.Title = "Document V2";
                    }

                });

And decorated my Controllers' Methods with

[ApiVersion("v2")]

[ApiVersion("v1")]

etc.

like image 784
shadowsheep Avatar asked Oct 24 '25 21:10

shadowsheep


2 Answers

You can define app.UseSwagger twice and implement a custom operation processor which filters only the desired api actions according to your needs (ie in the first call you should filter allversion x and in the second all versions of y).

The ApiVersionProcessor which is currently added by default only replaces the version placeholder in the route path with the first declared version. I think we should extend this processor so that you can exclude versions and also that the correct version is inserted.

Btw: I'm the author of NSwag.

like image 68
Rico Suter Avatar answered Oct 27 '25 02:10

Rico Suter


There is an out of the box solution now. I propose it for ASP.NET WebAPI Owin and I guess it should be very similar in ASP.NET Core.

First: You need to install ASP.NET API Versioning (GitHub, Nuget)

Second: You need to decorate your action methods with the desired route and version. For example:

[Route("api/v{version:apiVersion}/get1")]
[ApiVersion("1")]

public IEnumerable<string> Get1()
{           
   return new string[] { "value1", "value2" };
}

[Route("api/v{version:apiVersion}/get2")]
[ApiVersion("2")]

public IEnumerable<string> Get2()
{           
   return new string[] { "value1", "value2" };
}

Third: You got to add the required configuration to the Startup.cs file to 1) Generate a separate OpenAPI Specification file for each API version 2) Ask NSwag to show the sepecification files through Swagger-UI

public class Startup
{
    [Obsolete]
    public void Configuration(IAppBuilder app)
    {
        var config = new HttpConfiguration();

        // register versioning
        config.MapHttpAttributeRoutes(new DefaultInlineConstraintResolver
        {
            ConstraintMap = { ["apiVersion"] = typeof(ApiVersionRouteConstraint) }
        });

        config.AddApiVersioning(options =>
        {
            // reporting api versions will return the headers "api-supported-versions" and "api-deprecated-versions"
            options.ReportApiVersions = true;

            // automatically applies an api version based on the name of the defining controller's namespace
            options.Conventions.Add(new VersionByNamespaceConvention());
        });

        // generate OpenAPI Sepecification for each version and assign a route to it

        var assembly = typeof(Startup).Assembly;

        app.UseSwagger(assembly ,s =>
        {
            s.GeneratorSettings.OperationProcessors.TryGet<ApiVersionProcessor>().IncludedVersions = new[] { "1" };
            s.GeneratorSettings.SchemaType = SchemaType.OpenApi3;
            s.DocumentPath = "/swagger/v1.0.json";
        });

        app.UseSwagger(assembly , s =>
        {
            s.GeneratorSettings.OperationProcessors.TryGet<ApiVersionProcessor>().IncludedVersions = new[] { "2" };
            s.GeneratorSettings.SchemaType = SchemaType.OpenApi3;

            s.DocumentPath = "/swagger/v2.0.json";
        });

        // integrate Swagger-UI with the generated OpenAPI files generated before.
        _ = app.UseSwaggerUi3(assembly , s =>
          {
              s.SwaggerRoutes.Add(new SwaggerUi3Route("Version 1", "/swagger/v1.0.json"));
              s.SwaggerRoutes.Add(new SwaggerUi3Route("Version 2", "/swagger/v2.0.json"));

              s.GeneratorSettings.Title = "My API";
              s.GeneratorSettings.Description = "API functionalities.";
          });

        app.UseWebApi(config);

        config.EnsureInitialized();
    }
}

Go to the Swagger page. You'll See:

  1. There is a drop box on upper right that lets you select versions
  2. For each API Version, only the relevant action methods are listed.
like image 35
Ehsan Mirsaeedi Avatar answered Oct 27 '25 01:10

Ehsan Mirsaeedi