Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 39 additions & 27 deletions src/Swashbuckle.AspNetCore.Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,19 @@ public static int Main(string[] args)
{
c.Argument("startupassembly", "relative path to the application's startup assembly");
c.Argument("swaggerdoc", "name of the swagger doc you want to retrieve, as configured in your startup class");

c.Option("--output", "relative path where the Swagger will be output, defaults to stdout");
c.Option("--host", "a specific host to include in the Swagger output");
c.Option("--basepath", "a specific basePath to include in the Swagger output");
c.Option("--serializeasv2", "output Swagger in the V2 format rather than V3", true);
c.Option("--yaml", "exports swagger in a yaml format", true);

c.OnRun((namedArgs) =>
{
if (!File.Exists(namedArgs["startupassembly"]))
{
throw new FileNotFoundException(namedArgs["startupassembly"]);
}

var depsFile = namedArgs["startupassembly"].Replace(".dll", ".deps.json");
var runtimeConfig = namedArgs["startupassembly"].Replace(".dll", ".runtimeconfig.json");
Expand Down Expand Up @@ -110,37 +114,42 @@ public static int Main(string[] args)
}
}

using (Stream stream = outputPath != null ? File.Create(outputPath) : Console.OpenStandardOutput())
using (var streamWriter = new FormattingStreamWriter(stream, CultureInfo.InvariantCulture))
using Stream stream = outputPath != null ? File.Create(outputPath) : Console.OpenStandardOutput();
using var streamWriter = new FormattingStreamWriter(stream, CultureInfo.InvariantCulture);

IOpenApiWriter writer;
if (namedArgs.ContainsKey("--yaml"))
{
IOpenApiWriter writer;
if (namedArgs.ContainsKey("--yaml"))
writer = new OpenApiYamlWriter(streamWriter);
else
writer = new OpenApiJsonWriter(streamWriter);
writer = new OpenApiYamlWriter(streamWriter);
}
else
{
writer = new OpenApiJsonWriter(streamWriter);
}

if (namedArgs.ContainsKey("--serializeasv2"))
{
if (swaggerDocumentSerializer != null)
{
swaggerDocumentSerializer.SerializeDocument(swagger, writer, Microsoft.OpenApi.OpenApiSpecVersion.OpenApi2_0);
}
else
{
swagger.SerializeAsV2(writer);
}
}
else if (swaggerDocumentSerializer != null)
if (namedArgs.ContainsKey("--serializeasv2"))
{
if (swaggerDocumentSerializer != null)
{
swaggerDocumentSerializer.SerializeDocument(swagger, writer, Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_0);
swaggerDocumentSerializer.SerializeDocument(swagger, writer, Microsoft.OpenApi.OpenApiSpecVersion.OpenApi2_0);
}
else
{
swagger.SerializeAsV3(writer);
swagger.SerializeAsV2(writer);
}
}
else if (swaggerDocumentSerializer != null)
{
swaggerDocumentSerializer.SerializeDocument(swagger, writer, Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_0);
}
else
{
swagger.SerializeAsV3(writer);
}

if (outputPath != null)
Console.WriteLine($"Swagger JSON/YAML successfully written to {outputPath}");
if (outputPath != null)
{
Console.WriteLine($"Swagger JSON/YAML successfully written to {outputPath}");
}

return 0;
Expand All @@ -152,7 +161,7 @@ public static int Main(string[] args)

private static string EscapePath(string path)
{
return path.Contains(" ")
return path.Contains(' ')
? "\"" + path + "\""
: path;
}
Expand Down Expand Up @@ -200,23 +209,26 @@ private static bool TryGetCustomHost<THost>(
.Where(t => t.Name == factoryClassName)
.ToList();

if (!factoryTypes.Any())
if (factoryTypes.Count == 0)
{
host = default;
return false;
}

if (factoryTypes.Count() > 1)
else if (factoryTypes.Count > 1)
{
throw new InvalidOperationException($"Multiple {factoryClassName} classes detected");
}

var factoryMethod = factoryTypes
.Single()
.GetMethod(factoryMethodName, BindingFlags.Public | BindingFlags.Static);

if (factoryMethod == null || factoryMethod.ReturnType != typeof(THost))
{
throw new InvalidOperationException(
$"{factoryClassName} class detected but does not contain a public static method " +
$"called {factoryMethodName} with return type {typeof(THost).Name}");
}

host = (THost)factoryMethod.Invoke(null, null);
return true;
Expand Down
6 changes: 4 additions & 2 deletions src/Swashbuckle.AspNetCore.Swagger/IAsyncSwaggerProvider.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.OpenApi.Models;

Expand All @@ -8,6 +9,7 @@ public interface IAsyncSwaggerProvider
Task<OpenApiDocument> GetSwaggerAsync(
string documentName,
string host = null,
string basePath = null);
string basePath = null,
CancellationToken cancellationToken = default);
}
}
}
88 changes: 45 additions & 43 deletions src/Swashbuckle.AspNetCore.Swagger/SwaggerMiddleware.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Globalization;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading.Tasks;
Expand Down Expand Up @@ -57,17 +56,23 @@ public async Task Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvid
? httpContext.Request.PathBase.Value
: null;

var swagger = swaggerProvider switch
OpenApiDocument swagger;
var asyncSwaggerProvider = httpContext.RequestServices.GetService<IAsyncSwaggerProvider>();

if (asyncSwaggerProvider is not null)
{
IAsyncSwaggerProvider asyncSwaggerProvider => await asyncSwaggerProvider.GetSwaggerAsync(
swagger = await asyncSwaggerProvider.GetSwaggerAsync(
documentName: documentName,
host: null,
basePath: basePath),
_ => swaggerProvider.GetSwagger(
basePath: basePath);
}
else
{
swagger = swaggerProvider.GetSwagger(
documentName: documentName,
host: null,
basePath: basePath)
};
basePath: basePath);
}

// One last opportunity to modify the Swagger Document - this time with request context
foreach (var filter in _options.PreSerializeFilters)
Expand Down Expand Up @@ -123,7 +128,7 @@ private bool RequestingSwaggerDocument(HttpRequest request, out string documentN
return false;
}

private void RespondWithNotFound(HttpResponse response)
private static void RespondWithNotFound(HttpResponse response)
{
response.StatusCode = 404;
}
Expand All @@ -133,53 +138,50 @@ private async Task RespondWithSwaggerJson(HttpResponse response, OpenApiDocument
response.StatusCode = 200;
response.ContentType = "application/json;charset=utf-8";

using (var textWriter = new StringWriter(CultureInfo.InvariantCulture))
using var textWriter = new StringWriter(CultureInfo.InvariantCulture);
var jsonWriter = new OpenApiJsonWriter(textWriter);

if (_options.SerializeAsV2)
{
var jsonWriter = new OpenApiJsonWriter(textWriter);
if (_options.SerializeAsV2)
{
if (_options.CustomDocumentSerializer != null)
_options.CustomDocumentSerializer.SerializeDocument(swagger, jsonWriter, Microsoft.OpenApi.OpenApiSpecVersion.OpenApi2_0);
else
swagger.SerializeAsV2(jsonWriter);
}
if (_options.CustomDocumentSerializer != null)
_options.CustomDocumentSerializer.SerializeDocument(swagger, jsonWriter, Microsoft.OpenApi.OpenApiSpecVersion.OpenApi2_0);
else
{
if (_options.CustomDocumentSerializer != null)
_options.CustomDocumentSerializer.SerializeDocument(swagger, jsonWriter, Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_0);
else
swagger.SerializeAsV3(jsonWriter);
}

await response.WriteAsync(textWriter.ToString(), new UTF8Encoding(false));
swagger.SerializeAsV2(jsonWriter);
}
else
{
if (_options.CustomDocumentSerializer != null)
_options.CustomDocumentSerializer.SerializeDocument(swagger, jsonWriter, Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_0);
else
swagger.SerializeAsV3(jsonWriter);
}

await response.WriteAsync(textWriter.ToString(), new UTF8Encoding(false));
}

private async Task RespondWithSwaggerYaml(HttpResponse response, OpenApiDocument swagger)
{
response.StatusCode = 200;
response.ContentType = "text/yaml;charset=utf-8";

using (var textWriter = new StringWriter(CultureInfo.InvariantCulture))
using var textWriter = new StringWriter(CultureInfo.InvariantCulture);
var yamlWriter = new OpenApiYamlWriter(textWriter);
if (_options.SerializeAsV2)
{
var yamlWriter = new OpenApiYamlWriter(textWriter);
if (_options.SerializeAsV2)
{
if (_options.CustomDocumentSerializer != null)
_options.CustomDocumentSerializer.SerializeDocument(swagger, yamlWriter, Microsoft.OpenApi.OpenApiSpecVersion.OpenApi2_0);
else
swagger.SerializeAsV2(yamlWriter);
}
if (_options.CustomDocumentSerializer != null)
_options.CustomDocumentSerializer.SerializeDocument(swagger, yamlWriter, Microsoft.OpenApi.OpenApiSpecVersion.OpenApi2_0);
else
{
if (_options.CustomDocumentSerializer != null)
_options.CustomDocumentSerializer.SerializeDocument(swagger, yamlWriter, Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_0);
else
swagger.SerializeAsV3(yamlWriter);
}

await response.WriteAsync(textWriter.ToString(), new UTF8Encoding(false));
swagger.SerializeAsV2(yamlWriter);
}
else
{
if (_options.CustomDocumentSerializer != null)
_options.CustomDocumentSerializer.SerializeDocument(swagger, yamlWriter, Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_0);
else
swagger.SerializeAsV3(yamlWriter);
}

await response.WriteAsync(textWriter.ToString(), new UTF8Encoding(false));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,13 @@ public void DeepCopy(SwaggerGeneratorOptions source, SwaggerGeneratorOptions tar
target.SecuritySchemes = new Dictionary<string, OpenApiSecurityScheme>(source.SecuritySchemes);
target.SecurityRequirements = new List<OpenApiSecurityRequirement>(source.SecurityRequirements);
target.ParameterFilters = new List<IParameterFilter>(source.ParameterFilters);
target.ParameterAsyncFilters = new List<IParameterAsyncFilter>(source.ParameterAsyncFilters);
target.OperationFilters = new List<IOperationFilter>(source.OperationFilters);
target.OperationAsyncFilters = new List<IOperationAsyncFilter>(source.OperationAsyncFilters);
target.DocumentFilters = new List<IDocumentFilter>(source.DocumentFilters);
target.DocumentAsyncFilters = new List<IDocumentAsyncFilter>(source.DocumentAsyncFilters);
target.RequestBodyFilters = new List<IRequestBodyFilter>(source.RequestBodyFilters);
target.RequestBodyAsyncFilters = new List<IRequestBodyAsyncFilter>(source.RequestBodyAsyncFilters);
target.SecuritySchemesSelector = source.SecuritySchemesSelector;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ public static IServiceCollection AddSwaggerGen(
services.AddTransient<IConfigureOptions<SwaggerGeneratorOptions>, ConfigureSwaggerGeneratorOptions>();
services.AddTransient<IConfigureOptions<SchemaGeneratorOptions>, ConfigureSchemaGeneratorOptions>();

// Register generator and it's dependencies
services.TryAddTransient<ISwaggerProvider, SwaggerGenerator>();
services.TryAddTransient<IAsyncSwaggerProvider, SwaggerGenerator>();
// Register generator and its dependencies
services.TryAddTransient<SwaggerGenerator>();
services.TryAddTransient<ISwaggerProvider>(s => s.GetRequiredService<SwaggerGenerator>());
services.TryAddTransient<IAsyncSwaggerProvider>(s => s.GetRequiredService<SwaggerGenerator>());
services.TryAddTransient(s => s.GetRequiredService<IOptions<SwaggerGeneratorOptions>>().Value);
services.TryAddTransient<ISchemaGenerator, SchemaGenerator>();
services.TryAddTransient(s => s.GetRequiredService<IOptions<SchemaGeneratorOptions>>().Value);
Expand Down Expand Up @@ -53,13 +54,15 @@ public static void ConfigureSwaggerGen(

private sealed class JsonSerializerOptionsProvider
{
private readonly IServiceProvider _serviceProvider;
private JsonSerializerOptions _options;
#if !NETSTANDARD2_0
private readonly IServiceProvider _serviceProvider;

public JsonSerializerOptionsProvider(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
#endif

public JsonSerializerOptions Options => _options ??= ResolveOptions();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.OpenApi.Models;

Expand All @@ -9,6 +11,11 @@ public interface IDocumentFilter
void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context);
}

public interface IDocumentAsyncFilter
{
Task ApplyAsync(OpenApiDocument swaggerDoc, DocumentFilterContext context, CancellationToken cancellationToken);
}

public class DocumentFilterContext
{
public DocumentFilterContext(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.OpenApi.Models;

Expand All @@ -9,6 +11,11 @@ public interface IOperationFilter
void Apply(OpenApiOperation operation, OperationFilterContext context);
}

public interface IOperationAsyncFilter
{
Task ApplyAsync(OpenApiOperation operation, OperationFilterContext context, CancellationToken cancellationToken);
}

public class OperationFilterContext
{
public OperationFilterContext(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.OpenApi.Models;

Expand All @@ -9,6 +11,11 @@ public interface IParameterFilter
void Apply(OpenApiParameter parameter, ParameterFilterContext context);
}

public interface IParameterAsyncFilter
{
Task ApplyAsync(OpenApiParameter parameter, ParameterFilterContext context, CancellationToken cancellationToken);
}

public class ParameterFilterContext
{
public ParameterFilterContext(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.OpenApi.Models;

Expand All @@ -9,6 +11,11 @@ public interface IRequestBodyFilter
void Apply(OpenApiRequestBody requestBody, RequestBodyFilterContext context);
}

public interface IRequestBodyAsyncFilter
{
Task ApplyAsync(OpenApiRequestBody requestBody, RequestBodyFilterContext context, CancellationToken cancellationToken);
}

public class RequestBodyFilterContext
{
public RequestBodyFilterContext(
Expand Down
Loading