title: Generate multiple Swagger specs from ASP.NET Core 5.0 API solution
body: <p>I am currently setting up a solution in ASP.NET Core 5.0, in which I have multiple class library projects containing API controllers. The reason for this separation is that the solution has access to different systems within my employer's digital infrastructure. The solution contains one actual API project which is meant to combine all these modules.</p>
  <p>I am using Swagger and SwaggerUI to generate documentation and a spec for codegen in the API project, which works as expected. In this API project, Swagger is configured as such:</p>
  <p>Startup.cs:</p>
  <pre class="lang-cs prettyprint-override"><code>public void ConfigureServices(IServiceCollection services)
          {
              services.RegisterServicesFromModules(this.Configuration, GetModules());
  
              // API versioning
              {
                  services.AddApiVersioning(versioningOptions =&gt;
                  {
                      versioningOptions.ReportApiVersions = true;
                      versioningOptions.Conventions.Add(new VersionByNamespaceConvention());
                  });
  
                  services.AddVersionedApiExplorer(versionExplorerOptions =&gt;
                  {
                      versionExplorerOptions.GroupNameFormat = &quot;'v'VVV&quot;;
                      versionExplorerOptions.SubstituteApiVersionInUrl = true;
                  });
              }
              
              // Open API
              {
                  services.AddTransient&lt;IConfigureOptions&lt;SwaggerUIOptions&gt;, ConfigureSwaggerUIOptions&gt;();
                  services.AddTransient&lt;IConfigureOptions&lt;SwaggerGenOptions&gt;, ConfigureSwaggerGenOptions&gt;();
                  services.AddSwaggerGen();
              }
          }
  </code></pre>
  <p>(the <code>RegisterServicesFromModules()</code> method at the top of the snippet is a self defined extension method which calls other modules' DI containers and registers the services defined there)</p>
  <p>ConfigureSwaggerUIOptions.cs:</p>
  <pre class="lang-cs prettyprint-override"><code>public class ConfigureSwaggerUIOptions : IConfigureOptions&lt;SwaggerUIOptions&gt;
      {
          private readonly IApiVersionDescriptionProvider _apiProvider;
          private readonly string _appName;
  
          /// &lt;summary&gt;
          /// Initialises a new instance of the &lt;see cref=&quot;ConfigureSwaggerUIOptions&quot;/&gt; class.
          /// &lt;/summary&gt;
          /// &lt;param name=&quot;apiProvider&quot;&gt;The API provider.&lt;/param&gt;
          public ConfigureSwaggerUIOptions(IApiVersionDescriptionProvider apiProvider)
          {
              _apiProvider = apiProvider;
              _appName = Assembly.GetExecutingAssembly().GetName()?.Name ?? string.Empty;
          }
  
          /// &lt;inheritdoc /&gt;
          public void Configure(SwaggerUIOptions options)
          {
              options = options ?? throw new ArgumentNullException(nameof(options));
  
              options.RoutePrefix = &quot;docs&quot;;
              options.DocumentTitle = $&quot;\'{this._appName}\' documentation&quot;;
              options.DocExpansion(DocExpansion.List);
              options.DefaultModelExpandDepth(0);
  
              foreach (var apiVersionDescription in this._apiProvider.ApiVersionDescriptions)
              {
                  options.SwaggerEndpoint($&quot;/swagger/{apiVersionDescription.GroupName}/swagger.json&quot;, apiVersionDescription.GroupName);
              }
          }
      }
  </code></pre>
  <p>And, lastly, ConfigureSwaggerGenOptions.cs:</p>
  <pre class="lang-cs prettyprint-override"><code>public class ConfigureSwaggerGenOptions : IConfigureOptions&lt;SwaggerGenOptions&gt;
      {
          private readonly IApiVersionDescriptionProvider _apiProvider;
          private readonly string _appName;
  
          /// &lt;summary&gt;
          /// Initializes a new instance of the &lt;see cref=&quot;ConfigureSwaggerGenOptions&quot;/&gt; class.
          /// &lt;/summary&gt;
          /// &lt;param name=&quot;apiProvider&quot;&gt;The API Version Description.&lt;/param&gt;
          public ConfigureSwaggerGenOptions(IApiVersionDescriptionProvider apiProvider)
          {
              _apiProvider = apiProvider ?? throw new ArgumentNullException(nameof(apiProvider));
              _appName = Assembly.GetExecutingAssembly().GetName()?.Name ?? string.Empty;
          }
  
          /// &lt;inheritdoc /&gt;
          public void Configure(SwaggerGenOptions options)
          {
              options = options ?? throw new ArgumentNullException(nameof(options));
  
              foreach (var apiVersionDescription in this._apiProvider.ApiVersionDescriptions)
              {
                  options.SwaggerDoc(apiVersionDescription.GroupName, this.CreateInfoForApiVersion(apiVersionDescription));
  
                  if (!string.IsNullOrWhiteSpace(this._appName))
                  {
                      options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $&quot;{this._appName}.xml&quot;));
                  }
                  
                  options.OperationFilter&lt;SwaggerDefaultValues&gt;();
              }
          }
  
          private OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription apiVersionDescription)
          {
              var info = new OpenApiInfo()
              {
                  Title = this._appName,
                  Version = apiVersionDescription.ApiVersion.ToString(),
                  Description = $&quot;OpenAPI documentation for \'{this._appName}\'.&quot;,
                  Contact = new OpenApiContact() { Name = &quot;&lt;employer&gt;&quot;, Email = &quot;&lt;email&gt;&quot; },
              };
  
              if (apiVersionDescription.IsDeprecated)
              {
                  info.Description += &quot; This API version has been deprecated.&quot;;
              }
  
              return info;
          }
      }
  </code></pre>
  <p>Above files succesfully generate a swagger.json file and documentation interface at the specified URL. However, as I said, this generates overall documentation for the <em>entire</em> API. Some apps which are to communicate with the API however, need only have access to one single module and have no use to generate calls for the entire API.</p>
  <p>To facilitate this, I try to generate multiple swagger.json files at different URLs for each module respectively, containing only information about that specific module. As I said, each separate module contains its own DI container which is called by the <code>RegisterServicesFromModules</code> extension method. So for a specific module, the DI would look like this:</p>
  <p>DmsApiModule.cs:</p>
  <pre class="lang-cs prettyprint-override"><code>public class DmsApiModule : IModule, IModuleInjector
      {
          /// &lt;inheritdoc /&gt;
          public void ConfigureServices(IServiceCollection serviceCollection, IConfiguration configuration)
          {
              serviceCollection.RegisterServicesFromModules(configuration, GetModules());
  
              serviceCollection.AddControllers();
              
              // DMS API Versioning
              {
                  serviceCollection.AddApiVersioning(options =&gt;
                  {
                      options.ReportApiVersions = true;
                      options.Conventions.Add(new VersionByNamespaceConvention());
                  });
  
                  serviceCollection.AddVersionedApiExplorer(options =&gt;
                  {
                      options.GroupNameFormat = &quot;'v'VVV&quot;;
                      options.SubstituteApiVersionInUrl = true;
                  });
              }
              
              // DMS Open API
              {
                  serviceCollection.AddTransient&lt;IConfigureOptions&lt;SwaggerUIOptions&gt;, ConfigureDmsSwaggerUIOptions&gt;();
                  serviceCollection.AddTransient&lt;IConfigureOptions&lt;SwaggerGenOptions&gt;, ConfigureDmsSwaggerGenOptions&gt;();
              }
          }
  
          /// &lt;inheritdoc /&gt;
          public IEnumerable&lt;IModule&gt; GetModules()
          {
              yield return new DmsBusinessModule();
          }
      }
  </code></pre>
  <p>As can be seen, the service registration is almost identical to Startup.cs. The files ConfigureDmsSwaggerUIOptions.cs and ConfigureDmsSwaggerGenOptions.cs are also almost identical to the similarly named files above, apart from the different <code>SwaggerEndpoint</code> specified in the SwaggerUI configuration.
  The respective endpoints are:</p>
  <pre class="lang-cs prettyprint-override"><code>// Overall API: 
  options.SwaggerEndpoint($&quot;/swagger/{apiVersionDescription.GroupName}/swagger.json&quot;, apiVersionDescription.GroupName);
  // Module API:
  options.SwaggerEndpoint($&quot;/swagger/DMS.{apiVersionDescription.GroupName}/swagger.json&quot;, $&quot;DMS.{apiVersionDescription.GroupName}&quot;);
  </code></pre>
  <p>However, the module specific API contains no paths (it shows &quot;No operations defined in spec!&quot;), even though there definitely are operations defined and the overall API does detect them.</p>
  <p>I am unsure why this is happening. The module specific endpoint can be found since the OpenApiInfo is shown in the UI. Is there anything I am missing or doing wrong in my configuration? Or is Swagger just not capable of understanding this structure? (I read somewhere that Swagger tends to not like multiple endpoints very much when they are defined in manners similar to mine).</p>
  <p>Any help would be much appreciated! Please do not hesitate to leave any questions in the comments!</p>
  <p>Thanks in advance!</p>