Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swagger UI not rendering for an AOT project (e.g. Docker) #2776

Closed
dan-matthews opened this issue Apr 4, 2024 · 2 comments
Closed

Swagger UI not rendering for an AOT project (e.g. Docker) #2776

dan-matthews opened this issue Apr 4, 2024 · 2 comments
Labels
help-wanted A change up for grabs for contributions from the community

Comments

@dan-matthews
Copy link

The Swagger UI may not render if code is running using AOT compilation, for example in a Docker container. This is only seen when a 'real' AOT compilation is used.

Replication

To replicate (Visual Studio 2022):

  1. Create a new .Net 8 ASP.NET Core Web API (native AOT), targeting a Docker container
  2. Add Swashbuckle
  3. Run locally (Docker desktop) and access the swagger endpoint (this will work)
  4. Deploy a release build to a container (e.g. Azure Container Instance)
  5. Access the swagger endpoint, and the page will be blank, with an error in the console (typically an javascript undefined or similar error about the config object URLs)

Cause

This is caused by the fact that the SwaggerUIMiddleware creates a JSON serializer that doesn't have any context for the ConfigObject and OAuthConfigObject classes. These are unavailable to the serializer once AOT compilation occurs, so an empty object is written in the javascript configObject object. This then means that the javascript errors when it tries to get the URLs (see the Swashbuckle.AspNetCore.SwaggerUI.index.html resource).

Workaround

With some slightly ugly code, a 'fixed' version of the Swagger UI Middleware can be used:

using Microsoft.Extensions.Options;
using Swashbuckle.AspNetCore.SwaggerUI;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace SwaggerFix;

public class SwaggerUIMiddlewareFix : SwaggerUIMiddleware
{
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_jsonSerializerOptions")]
extern static ref JsonSerializerOptions GetJsonSerializerOptions(SwaggerUIMiddleware @this);

public SwaggerUIMiddlewareFix(RequestDelegate next, IWebHostEnvironment hostingEnv, ILoggerFactory loggerFactory, SwaggerUIOptions options) : base(next, hostingEnv, loggerFactory, options)
{
    var jsonSerializerOptions = GetJsonSerializerOptions(this);

    jsonSerializerOptions.TypeInfoResolverChain.Insert(0, SwaggerSerializerContext.Default);
}

}

[JsonSerializable(typeof(ConfigObject))]
[JsonSerializable(typeof(OAuthConfigObject))]
public partial class SwaggerSerializerContext : JsonSerializerContext { }

public static class SwaggerUIMiddlewareFixExtensions
{
public static IApplicationBuilder UseSwaggerUIFix(this IApplicationBuilder app, Action setupAction = null)
{
SwaggerUIOptions value;
using (IServiceScope serviceScope = app.ApplicationServices.CreateScope())
{
value = serviceScope.ServiceProvider.GetRequiredService<IOptionsSnapshot>().Value;
setupAction?.Invoke(value);
}
if (value.ConfigObject.Urls == null)
{
IWebHostEnvironment requiredService = app.ApplicationServices.GetRequiredService();
value.ConfigObject.Urls = new UrlDescriptor[1]
{
new UrlDescriptor
{
Name = requiredService.ApplicationName + " v1",
Url = "v1/swagger.json"
}
};
}
return app.UseSwaggerUIFix(value);
}

public static IApplicationBuilder UseSwaggerUIFix(this IApplicationBuilder app, SwaggerUIOptions options)
{
    return app.UseMiddleware<SwaggerUIMiddlewareFix>(new object[1] { options });
}

}

When this class is used, then the 'fixed' extension should be used instead of the usual UseSwaggerUI extension. e.g.:

app.UseSwagger(); app.UseSwaggerUIFix(o => { o.SwaggerEndpoint("/swagger/v1/swagger.json", "v1"); o.RoutePrefix = string.Empty; });

Versions

Swashbuckle.AspNetCore 6.5.0
.Net 8

@Havunen
Copy link

Havunen commented Apr 13, 2024

Use DotSwashbuckle instead

@martincostello martincostello added the help-wanted A change up for grabs for contributions from the community label Apr 14, 2024
@martincostello
Copy link
Collaborator

Duplicate of #2550

@martincostello martincostello marked this as a duplicate of #2550 Apr 14, 2024
@martincostello martincostello closed this as not planned Won't fix, can't repro, duplicate, stale Apr 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help-wanted A change up for grabs for contributions from the community
Projects
None yet
Development

No branches or pull requests

3 participants