From 68b11220a8191e5eba6974962edf3456cfde4187 Mon Sep 17 00:00:00 2001
From: Nils Henrik Hals <3185998+Strepto@users.noreply.github.com>
Date: Wed, 4 Mar 2026 15:04:51 +0000
Subject: [PATCH 01/11] Add MapSwaggerUI support and related tests for endpoint
routing
---
Swashbuckle.AspNetCore.slnx | 1 +
docs/configure-and-customize-swaggerui.md | 22 +++++++
.../PublicAPI/PublicAPI.Unshipped.txt | 2 +-
.../SwaggerUIBuilderExtensions.cs | 64 ++++++++++++++++---
.../SwaggerAndSwaggerUIIntegrationTests.cs | 15 +++++
test/WebSites/WebApi.Map/Program.cs | 62 ++++++++++++++++++
.../WebApi.Map/Properties/launchSettings.json | 41 ++++++++++++
test/WebSites/WebApi.Map/WebApi.Map.csproj | 16 +++++
test/WebSites/WebApi.Map/WebApi.http | 4 ++
.../WebApi.Map/appsettings.Development.json | 8 +++
test/WebSites/WebApi.Map/appsettings.json | 9 +++
11 files changed, 233 insertions(+), 11 deletions(-)
create mode 100644 test/WebSites/WebApi.Map/Program.cs
create mode 100644 test/WebSites/WebApi.Map/Properties/launchSettings.json
create mode 100644 test/WebSites/WebApi.Map/WebApi.Map.csproj
create mode 100644 test/WebSites/WebApi.Map/WebApi.http
create mode 100644 test/WebSites/WebApi.Map/appsettings.Development.json
create mode 100644 test/WebSites/WebApi.Map/appsettings.json
diff --git a/Swashbuckle.AspNetCore.slnx b/Swashbuckle.AspNetCore.slnx
index 5ba846053c..a6e0f06356 100644
--- a/Swashbuckle.AspNetCore.slnx
+++ b/Swashbuckle.AspNetCore.slnx
@@ -80,6 +80,7 @@
+
diff --git a/docs/configure-and-customize-swaggerui.md b/docs/configure-and-customize-swaggerui.md
index 8dfcf90efa..4c3c5daef3 100644
--- a/docs/configure-and-customize-swaggerui.md
+++ b/docs/configure-and-customize-swaggerui.md
@@ -244,3 +244,25 @@ app.UseSwaggerUI(options =>
[swagger-ui]: https://github.com/swagger-api/swagger-ui
+
+## Use `MapSwaggerUI` with endpoint routing
+
+`MapSwaggerUI` is an endpoint-routing alternative to `UseSwaggerUI`. The options API is the same, so all customization examples in this page also apply when using `MapSwaggerUI`.
+
+```cs
+app.MapSwagger();
+app.MapSwaggerUI(options =>
+{
+ options.SwaggerEndpoint("/swagger/v1/swagger.json", "V1 Docs");
+});
+```
+
+The main differences are:
+
+- **Use**SwaggerUI adds middleware directly to the request pipeline and returns `IApplicationBuilder`.
+- **Map**SwaggerUI maps the Swagger UI via endpoint routing and returns `IEndpointConventionBuilder`.
+
+- Because `MapSwaggerUI` returns an endpoint convention builder, you can attach endpoint metadata/conventions (for example, authorization policies) directly to the mapped UI endpoint.
+ ```csharp
+ app.MapSwaggerUI().RequireAuthorization();
+ ```
diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/PublicAPI/PublicAPI.Unshipped.txt b/src/Swashbuckle.AspNetCore.SwaggerUI/PublicAPI/PublicAPI.Unshipped.txt
index 5f282702bb..09764248dd 100644
--- a/src/Swashbuckle.AspNetCore.SwaggerUI/PublicAPI/PublicAPI.Unshipped.txt
+++ b/src/Swashbuckle.AspNetCore.SwaggerUI/PublicAPI/PublicAPI.Unshipped.txt
@@ -1 +1 @@
-
\ No newline at end of file
+static Microsoft.AspNetCore.Builder.SwaggerUIBuilderExtensions.MapSwaggerUI(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, System.Action setupAction = null) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder
diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIBuilderExtensions.cs b/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIBuilderExtensions.cs
index 8855eb65dd..61de191fa0 100644
--- a/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIBuilderExtensions.cs
+++ b/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIBuilderExtensions.cs
@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Swashbuckle.AspNetCore.SwaggerUI;
@@ -20,20 +21,63 @@ public static IApplicationBuilder UseSwaggerUI(
this IApplicationBuilder app,
Action setupAction = null)
{
- SwaggerUIOptions options;
- using (var scope = app.ApplicationServices.CreateScope())
- {
- options = scope.ServiceProvider.GetRequiredService>().Value;
- setupAction?.Invoke(options);
- }
+ var options = ResolveOptions(app.ApplicationServices, setupAction);
+ var hostingEnv = app.ApplicationServices.GetRequiredService();
+
+ EnsureDefaultUrl(options, hostingEnv.ApplicationName);
+
+ return app.UseSwaggerUI(options);
+ }
+
+ ///
+ /// Maps the SwaggerUI middleware to the specified endpoint route.
+ ///
+ ///
+ /// This is a convenience extension method that combines the registration of the SwaggerUI middleware with endpoint
+ /// routing. It allows you to specify the route pattern for the SwaggerUI and optionally configure the SwaggerUIOptions.
+ ///
+ /// Endpoint route builder to which the SwaggerUI middleware will be mapped.
+ /// Optional setup action to configure the SwaggerUIOptions.
+ /// An that can be used to further configure the endpoint.
+ public static IEndpointConventionBuilder MapSwaggerUI(
+ this IEndpointRouteBuilder endpoints,
+ Action setupAction = null)
+ {
+ var options = ResolveOptions(endpoints.ServiceProvider, setupAction);
+ var hostingEnv = endpoints.ServiceProvider.GetRequiredService();
+ EnsureDefaultUrl(options, hostingEnv.ApplicationName);
+ var pipeline = endpoints.CreateApplicationBuilder()
+ .UseSwaggerUI(options)
+ .Build();
+
+ return endpoints.Map(GetRoutePattern(options.RoutePrefix), pipeline);
+ }
+
+ private static SwaggerUIOptions ResolveOptions(IServiceProvider serviceProvider, Action setupAction)
+ {
// To simplify the common case, use a default that will work with the SwaggerMiddleware defaults
- if (options.ConfigObject.Urls == null)
+ using var scope = serviceProvider.CreateScope();
+ var options = scope.ServiceProvider.GetRequiredService>().Value;
+ setupAction?.Invoke(options);
+ return options;
+ }
+
+ private static void EnsureDefaultUrl(SwaggerUIOptions options, string applicationName)
+ {
+ if (options.ConfigObject.Urls != null)
{
- var hostingEnv = app.ApplicationServices.GetRequiredService();
- options.ConfigObject.Urls = [new UrlDescriptor { Name = $"{hostingEnv.ApplicationName} v1", Url = "v1/swagger.json" }];
+ return;
}
- return app.UseSwaggerUI(options);
+ options.ConfigObject.Urls = [new UrlDescriptor { Name = $"{applicationName} v1", Url = "v1/swagger.json" }];
+ }
+
+ private static string GetRoutePattern(string routePrefix)
+ {
+ var sanitizedRoutePrefix = routePrefix?.Trim('/');
+ return string.IsNullOrEmpty(sanitizedRoutePrefix)
+ ? "{**path}"
+ : $"{sanitizedRoutePrefix}/{{**path}}";
}
}
diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerAndSwaggerUIIntegrationTests.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerAndSwaggerUIIntegrationTests.cs
index ad3e8d9400..b53a2e61b8 100644
--- a/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerAndSwaggerUIIntegrationTests.cs
+++ b/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerAndSwaggerUIIntegrationTests.cs
@@ -19,4 +19,19 @@ public async Task SwaggerDocWithoutSubdirectory(string path, string mediaType)
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(mediaType, response.Content.Headers.ContentType?.MediaType);
}
+
+ [Theory]
+ [InlineData("/swagger/index.html", "text/html")]
+ [InlineData("/swagger/v1/swagger.json", "application/json")]
+ [InlineData("/swagger/v1/swagger.yaml", "text/yaml")]
+ [InlineData("/swagger/v1/swagger.yml", "text/yaml")]
+ public async Task MapSwaggerUI_And_MapSwagger_ReturnExpectedEndpoints(string path, string mediaType)
+ {
+ var client = new WebApplicationFactory().CreateClient();
+
+ var response = await client.GetAsync(path, TestContext.Current.CancellationToken);
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal(mediaType, response.Content.Headers.ContentType?.MediaType);
+ }
}
diff --git a/test/WebSites/WebApi.Map/Program.cs b/test/WebSites/WebApi.Map/Program.cs
new file mode 100644
index 0000000000..9a83cfc8ed
--- /dev/null
+++ b/test/WebSites/WebApi.Map/Program.cs
@@ -0,0 +1,62 @@
+using System.Reflection;
+using System.Text.Json.Serialization;
+using Microsoft.AspNetCore.Http.Json;
+
+// This exists just to test and verify that the MapSwagger and MapSwaggerUI works as expected.
+
+var builder = WebApplication.CreateBuilder(args);
+
+builder.Services.Configure(
+ options => options.SerializerOptions.Converters.Add(new JsonStringEnumConverter())
+);
+builder.Services.AddEndpointsApiExplorer();
+builder.Services.AddSwaggerGen(options =>
+{
+ options.SwaggerDoc("v1", new() { Title = "WebApi", Version = "v1" });
+});
+
+var app = builder.Build();
+
+app.MapSwagger();
+app.MapSwaggerUI();
+
+app.UseHttpsRedirection();
+
+string[] summaries =
+[
+ "Freezing",
+ "Bracing",
+ "Chilly",
+ "Cool",
+ "Mild",
+ "Warm",
+ "Balmy",
+ "Hot",
+ "Sweltering",
+ "Scorching",
+];
+
+app.MapGet("/weatherforecast", () =>
+{
+ var forecast = Enumerable.Range(1, 5).Select(index =>
+ new {
+ Date = DateTime.Now.AddDays(index),
+ TemperatureC = Random.Shared.Next(-20, 55),
+ Summary = summaries[Random.Shared.Next(summaries.Length)]
+ }
+ )
+ .ToArray();
+ return forecast;
+});
+
+app.Run();
+
+namespace WebApi.Map
+{
+ ///
+ /// Expose the Program class for use with WebApplicationFactory
+ ///
+ public partial class Program
+ {
+ }
+}
diff --git a/test/WebSites/WebApi.Map/Properties/launchSettings.json b/test/WebSites/WebApi.Map/Properties/launchSettings.json
new file mode 100644
index 0000000000..82bdd832be
--- /dev/null
+++ b/test/WebSites/WebApi.Map/Properties/launchSettings.json
@@ -0,0 +1,41 @@
+{
+ "$schema": "http://json.schemastore.org/launchsettings.json",
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:21394",
+ "sslPort": 44373
+ }
+ },
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "http://localhost:5205",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "https://localhost:7175;http://localhost:5205",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/test/WebSites/WebApi.Map/WebApi.Map.csproj b/test/WebSites/WebApi.Map/WebApi.Map.csproj
new file mode 100644
index 0000000000..f1cca10bfd
--- /dev/null
+++ b/test/WebSites/WebApi.Map/WebApi.Map.csproj
@@ -0,0 +1,16 @@
+
+
+
+ enable
+ enable
+ WebApi.Map
+ $(DefaultTargetFrameworks)
+ true
+
+
+
+
+
+
+
+
diff --git a/test/WebSites/WebApi.Map/WebApi.http b/test/WebSites/WebApi.Map/WebApi.http
new file mode 100644
index 0000000000..abc2f6184b
--- /dev/null
+++ b/test/WebSites/WebApi.Map/WebApi.http
@@ -0,0 +1,4 @@
+@WebApi_HostAddress = http://localhost:5205
+
+GET {{WebApi_HostAddress}}/weatherforecast/
+Accept: application/json
diff --git a/test/WebSites/WebApi.Map/appsettings.Development.json b/test/WebSites/WebApi.Map/appsettings.Development.json
new file mode 100644
index 0000000000..0c208ae918
--- /dev/null
+++ b/test/WebSites/WebApi.Map/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/test/WebSites/WebApi.Map/appsettings.json b/test/WebSites/WebApi.Map/appsettings.json
new file mode 100644
index 0000000000..10f68b8c8b
--- /dev/null
+++ b/test/WebSites/WebApi.Map/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
From 361f32ea3afd2dcd8ade6b486c97d3cd0b5fbf4f Mon Sep 17 00:00:00 2001
From: Nils Henrik Hals <3185998+Strepto@users.noreply.github.com>
Date: Tue, 10 Mar 2026 15:57:07 +0100
Subject: [PATCH 02/11] Refactor EnsureDefaultUrl
---
.../SwaggerUIBuilderExtensions.cs | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIBuilderExtensions.cs b/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIBuilderExtensions.cs
index 61de191fa0..69c60ca6d3 100644
--- a/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIBuilderExtensions.cs
+++ b/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIBuilderExtensions.cs
@@ -56,7 +56,6 @@ public static IEndpointConventionBuilder MapSwaggerUI(
private static SwaggerUIOptions ResolveOptions(IServiceProvider serviceProvider, Action setupAction)
{
- // To simplify the common case, use a default that will work with the SwaggerMiddleware defaults
using var scope = serviceProvider.CreateScope();
var options = scope.ServiceProvider.GetRequiredService>().Value;
setupAction?.Invoke(options);
@@ -65,12 +64,8 @@ private static SwaggerUIOptions ResolveOptions(IServiceProvider serviceProvider,
private static void EnsureDefaultUrl(SwaggerUIOptions options, string applicationName)
{
- if (options.ConfigObject.Urls != null)
- {
- return;
- }
-
- options.ConfigObject.Urls = [new UrlDescriptor { Name = $"{applicationName} v1", Url = "v1/swagger.json" }];
+ // To simplify the common case, use a default that will work with the SwaggerMiddleware defaults
+ options.ConfigObject.Urls ??= [new UrlDescriptor { Name = $"{applicationName} v1", Url = "v1/swagger.json" }];
}
private static string GetRoutePattern(string routePrefix)
From 33e70acce1f3a1a8279da8e1bdecf24516f588e5 Mon Sep 17 00:00:00 2001
From: Nils Henrik Hals <3185998+Strepto@users.noreply.github.com>
Date: Tue, 10 Mar 2026 15:57:19 +0100
Subject: [PATCH 03/11] Simplify launchSettings
---
.../WebApi.Map/Properties/launchSettings.json | 28 +------------------
1 file changed, 1 insertion(+), 27 deletions(-)
diff --git a/test/WebSites/WebApi.Map/Properties/launchSettings.json b/test/WebSites/WebApi.Map/Properties/launchSettings.json
index 82bdd832be..c003abfed9 100644
--- a/test/WebSites/WebApi.Map/Properties/launchSettings.json
+++ b/test/WebSites/WebApi.Map/Properties/launchSettings.json
@@ -1,38 +1,12 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
- "iisSettings": {
- "windowsAuthentication": false,
- "anonymousAuthentication": true,
- "iisExpress": {
- "applicationUrl": "http://localhost:21394",
- "sslPort": 44373
- }
- },
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
- "applicationUrl": "http://localhost:5205",
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development"
- }
- },
- "https": {
- "commandName": "Project",
- "dotnetRunMessages": true,
- "launchBrowser": true,
- "launchUrl": "swagger",
- "applicationUrl": "https://localhost:7175;http://localhost:5205",
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development"
- }
- },
- "IIS Express": {
- "commandName": "IISExpress",
- "launchBrowser": true,
- "launchUrl": "swagger",
+ "applicationUrl": "http://localhost:5154",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
From ffb87f385d755deebd7105b8e92d6397f58cb749 Mon Sep 17 00:00:00 2001
From: Nils Henrik Hals <3185998+Strepto@users.noreply.github.com>
Date: Tue, 10 Mar 2026 15:57:28 +0100
Subject: [PATCH 04/11] Fix spelling
---
docs/configure-and-customize-swaggerui.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/configure-and-customize-swaggerui.md b/docs/configure-and-customize-swaggerui.md
index 4c3c5daef3..084416619b 100644
--- a/docs/configure-and-customize-swaggerui.md
+++ b/docs/configure-and-customize-swaggerui.md
@@ -247,7 +247,7 @@ app.UseSwaggerUI(options =>
## Use `MapSwaggerUI` with endpoint routing
-`MapSwaggerUI` is an endpoint-routing alternative to `UseSwaggerUI`. The options API is the same, so all customization examples in this page also apply when using `MapSwaggerUI`.
+`MapSwaggerUI` is an endpoint-routing alternative to `UseSwaggerUI`. The options API is the same, so all customization examples on this page also apply when using `MapSwaggerUI`.
```cs
app.MapSwagger();
From 191edef732667df520b40159232f3ce95aba7420 Mon Sep 17 00:00:00 2001
From: Nils Henrik Hals <3185998+Strepto@users.noreply.github.com>
Date: Tue, 10 Mar 2026 22:59:31 +0100
Subject: [PATCH 05/11] Add MapReDoc
---
.../PublicAPI/PublicAPI.Unshipped.txt | 2 +-
.../ReDocBuilderExtensions.cs | 52 ++++++++++++++++---
2 files changed, 45 insertions(+), 9 deletions(-)
diff --git a/src/Swashbuckle.AspNetCore.ReDoc/PublicAPI/PublicAPI.Unshipped.txt b/src/Swashbuckle.AspNetCore.ReDoc/PublicAPI/PublicAPI.Unshipped.txt
index 5f282702bb..2263291395 100644
--- a/src/Swashbuckle.AspNetCore.ReDoc/PublicAPI/PublicAPI.Unshipped.txt
+++ b/src/Swashbuckle.AspNetCore.ReDoc/PublicAPI/PublicAPI.Unshipped.txt
@@ -1 +1 @@
-
\ No newline at end of file
+static Microsoft.AspNetCore.Builder.ReDocBuilderExtensions.MapReDoc(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, System.Action setupAction = null) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder
diff --git a/src/Swashbuckle.AspNetCore.ReDoc/ReDocBuilderExtensions.cs b/src/Swashbuckle.AspNetCore.ReDoc/ReDocBuilderExtensions.cs
index cb89a7748d..8d93ebbdc4 100644
--- a/src/Swashbuckle.AspNetCore.ReDoc/ReDocBuilderExtensions.cs
+++ b/src/Swashbuckle.AspNetCore.ReDoc/ReDocBuilderExtensions.cs
@@ -1,4 +1,5 @@
-using Microsoft.Extensions.DependencyInjection;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Swashbuckle.AspNetCore.ReDoc;
@@ -21,16 +22,51 @@ public static IApplicationBuilder UseReDoc(
this IApplicationBuilder app,
Action setupAction = null)
{
- ReDocOptions options;
- using (var scope = app.ApplicationServices.CreateScope())
- {
- options = scope.ServiceProvider.GetRequiredService>().Value;
- setupAction?.Invoke(options);
- }
+ var options = ResolveOptions(app.ApplicationServices, setupAction);
+ EnsureDefaultSpecUrl(options);
+ return app.UseReDoc(options);
+ }
+
+ ///
+ /// Maps the Redoc middleware to the specified endpoint route.
+ ///
+ /// Endpoint route builder to which the Redoc middleware will be mapped.
+ /// Optional setup action to configure the Redoc options.
+ /// An that can be used to further configure the endpoint.
+ public static IEndpointConventionBuilder MapReDoc(
+ this IEndpointRouteBuilder endpoints,
+ Action setupAction = null)
+ {
+ var options = ResolveOptions(endpoints.ServiceProvider, setupAction);
+ EnsureDefaultSpecUrl(options);
+
+ var pipeline = endpoints.CreateApplicationBuilder()
+ .UseReDoc(options)
+ .Build();
+
+ return endpoints.Map(GetRoutePattern(options.RoutePrefix), pipeline);
+ }
+
+ private static ReDocOptions ResolveOptions(IServiceProvider serviceProvider, Action setupAction)
+ {
+ using var scope = serviceProvider.CreateScope();
+ var options = scope.ServiceProvider.GetRequiredService>().Value;
+ setupAction?.Invoke(options);
+ return options;
+ }
+
+ private static void EnsureDefaultSpecUrl(ReDocOptions options)
+ {
// To simplify the common case, use a default that will work with the SwaggerMiddleware defaults
options.SpecUrl ??= "../swagger/v1/swagger.json";
+ }
- return app.UseReDoc(options);
+ private static string GetRoutePattern(string routePrefix)
+ {
+ var sanitizedRoutePrefix = routePrefix?.Trim('/');
+ return string.IsNullOrEmpty(sanitizedRoutePrefix)
+ ? "{**path}"
+ : $"{sanitizedRoutePrefix}/{{**path}}";
}
}
From 89e0c1fa63f63da02ba70827d7ff418200d4a645 Mon Sep 17 00:00:00 2001
From: Nils Henrik Hals <3185998+Strepto@users.noreply.github.com>
Date: Tue, 10 Mar 2026 23:00:15 +0100
Subject: [PATCH 06/11] Add tests and doc links for MapReDoc and MapSwaggerUI
with authorization
---
docs/configure-and-customize-redoc.md | 32 +++++++++++++++
docs/configure-and-customize-swaggerui.md | 24 ++++++++----
.../SwaggerAndSwaggerUIIntegrationTests.cs | 21 +++++++++-
test/WebSites/WebApi.Map/Program.cs | 39 ++++++++++++++-----
test/WebSites/WebApi.Map/WebApi.Map.csproj | 1 +
5 files changed, 99 insertions(+), 18 deletions(-)
diff --git a/docs/configure-and-customize-redoc.md b/docs/configure-and-customize-redoc.md
index 82f24a8166..718d515428 100644
--- a/docs/configure-and-customize-redoc.md
+++ b/docs/configure-and-customize-redoc.md
@@ -135,3 +135,35 @@ app.UseReDoc(options =>
> To get started, you should base your custom `index.html` on the [default version](../src/Swashbuckle.AspNetCore.ReDoc/index.html).
[redoc-options]: https://github.com/Redocly/redoc/blob/main/docs/deployment/html.md#the-redoc-object
+
+
+## Use `MapReDoc` with endpoint routing
+
+`MapReDoc` is an endpoint-routing alternative to `UseReDoc`. The options API is the same, so all customization examples on this page also apply when using `MapReDoc`.
+
+
+
+
+```cs
+app.MapReDoc();
+```
+snippet source | anchor
+
+
+
+The main differences are:
+
+- **Use**ReDoc adds middleware directly to the request pipeline and returns `IApplicationBuilder`.
+- **Map**ReDoc maps ReDoc via endpoint routing and returns `IEndpointConventionBuilder`.
+
+- Because `MapReDoc` returns an endpoint convention builder, you can attach endpoint metadata/conventions (for example, authorization policies) directly to the mapped ReDoc endpoint.
+
+
+
+```cs
+app.MapReDoc(reDocOptions => { reDocOptions.RoutePrefix = "redoc-auth"; })
+ .RequireAuthorization(); // Remember to also add RequireAuthorization to MapSwagger
+```
+snippet source | anchor
+
+
diff --git a/docs/configure-and-customize-swaggerui.md b/docs/configure-and-customize-swaggerui.md
index 084416619b..4501c20ba7 100644
--- a/docs/configure-and-customize-swaggerui.md
+++ b/docs/configure-and-customize-swaggerui.md
@@ -249,13 +249,16 @@ app.UseSwaggerUI(options =>
`MapSwaggerUI` is an endpoint-routing alternative to `UseSwaggerUI`. The options API is the same, so all customization examples on this page also apply when using `MapSwaggerUI`.
+
+
+
```cs
app.MapSwagger();
-app.MapSwaggerUI(options =>
-{
- options.SwaggerEndpoint("/swagger/v1/swagger.json", "V1 Docs");
-});
+app.MapSwaggerUI(options => { options.SwaggerEndpoint("/swagger/v1/swagger.json", "V1 Docs"); });
```
+snippet source | anchor
+
+
The main differences are:
@@ -263,6 +266,13 @@ The main differences are:
- **Map**SwaggerUI maps the Swagger UI via endpoint routing and returns `IEndpointConventionBuilder`.
- Because `MapSwaggerUI` returns an endpoint convention builder, you can attach endpoint metadata/conventions (for example, authorization policies) directly to the mapped UI endpoint.
- ```csharp
- app.MapSwaggerUI().RequireAuthorization();
- ```
+
+
+
+```cs
+app.MapSwaggerUI(o => { o.RoutePrefix = "swagger-auth"; })
+ .RequireAuthorization(); // Remember to also add RequireAuthorization to MapSwagger.
+```
+snippet source | anchor
+
+
diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerAndSwaggerUIIntegrationTests.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerAndSwaggerUIIntegrationTests.cs
index b53a2e61b8..8dd3c4e71b 100644
--- a/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerAndSwaggerUIIntegrationTests.cs
+++ b/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerAndSwaggerUIIntegrationTests.cs
@@ -21,11 +21,16 @@ public async Task SwaggerDocWithoutSubdirectory(string path, string mediaType)
}
[Theory]
- [InlineData("/swagger/index.html", "text/html")]
+ // MapSwagger()
[InlineData("/swagger/v1/swagger.json", "application/json")]
[InlineData("/swagger/v1/swagger.yaml", "text/yaml")]
[InlineData("/swagger/v1/swagger.yml", "text/yaml")]
- public async Task MapSwaggerUI_And_MapSwagger_ReturnExpectedEndpoints(string path, string mediaType)
+ // MapSwaggerUI()
+ [InlineData("/swagger/index.html", "text/html")]
+ // MapReDoc()
+ [InlineData("/api-docs/index.html", "text/html")]
+ [InlineData("/api-docs/index.js", "application/javascript")]
+ public async Task Map_Methods_ReturnExpectedEndpoints(string path, string mediaType)
{
var client = new WebApplicationFactory().CreateClient();
@@ -34,4 +39,16 @@ public async Task MapSwaggerUI_And_MapSwagger_ReturnExpectedEndpoints(string pat
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(mediaType, response.Content.Headers.ContentType?.MediaType);
}
+
+ [Theory]
+ // MapSwaggerUI().RequireAuthorization()
+ [InlineData("/swagger-auth/index.html")]
+ // MapReDoc().RequireAuthorization()
+ [InlineData("/redoc-auth/index.html")]
+ public async Task MapSwaggerUI_And_MapReDoc_RequireAuthorization_ReturnUnauthorized(string path)
+ {
+ var client = new WebApplicationFactory().CreateClient();
+ var response = await client.GetAsync(path, TestContext.Current.CancellationToken);
+ Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
+ }
}
diff --git a/test/WebSites/WebApi.Map/Program.cs b/test/WebSites/WebApi.Map/Program.cs
index 9a83cfc8ed..0eefea33ae 100644
--- a/test/WebSites/WebApi.Map/Program.cs
+++ b/test/WebSites/WebApi.Map/Program.cs
@@ -1,4 +1,3 @@
-using System.Reflection;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Http.Json;
@@ -6,19 +5,40 @@
var builder = WebApplication.CreateBuilder(args);
-builder.Services.Configure(
- options => options.SerializerOptions.Converters.Add(new JsonStringEnumConverter())
+builder.Services.Configure(options => options.SerializerOptions.Converters.Add(new JsonStringEnumConverter())
);
builder.Services.AddEndpointsApiExplorer();
-builder.Services.AddSwaggerGen(options =>
-{
- options.SwaggerDoc("v1", new() { Title = "WebApi", Version = "v1" });
-});
+builder.Services.AddSwaggerGen(options => { options.SwaggerDoc("v1", new() { Title = "WebApi", Version = "v1" }); });
+
+// Authentication and Authorization are added to verify that MapSwaggerUI and MapReDoc work as expected when RequireAuthorization is used.
+builder.Services.AddAuthentication().AddBearerToken();
+builder.Services.AddAuthorization();
var app = builder.Build();
+app.UseAuthentication();
+app.UseAuthorization();
+
+// begin-snippet: SwaggerUI-MapSwaggerUI
app.MapSwagger();
-app.MapSwaggerUI();
+app.MapSwaggerUI(options => { options.SwaggerEndpoint("/swagger/v1/swagger.json", "V1 Docs"); });
+// end-snippet
+
+// begin-snippet: SwaggerUI-MapSwaggerUI-RequireAuthorization
+app.MapSwaggerUI(o => { o.RoutePrefix = "swagger-auth"; })
+ .RequireAuthorization(); // Remember to also add RequireAuthorization to MapSwagger.
+// end-snippet
+
+
+// Also Map redoc in the same file
+// begin-snippet: Redoc-MapReDoc
+app.MapReDoc();
+// end-snippet
+
+// begin-snippet: Redoc-MapReDoc-RequireAuthorization
+app.MapReDoc(reDocOptions => { reDocOptions.RoutePrefix = "redoc-auth"; })
+ .RequireAuthorization(); // Remember to also add RequireAuthorization to MapSwagger
+// end-snippet
app.UseHttpsRedirection();
@@ -39,7 +59,8 @@
app.MapGet("/weatherforecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
- new {
+ new
+ {
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
diff --git a/test/WebSites/WebApi.Map/WebApi.Map.csproj b/test/WebSites/WebApi.Map/WebApi.Map.csproj
index f1cca10bfd..ccee2d6d9e 100644
--- a/test/WebSites/WebApi.Map/WebApi.Map.csproj
+++ b/test/WebSites/WebApi.Map/WebApi.Map.csproj
@@ -10,6 +10,7 @@
+
From c9a2d704ba9e4fb391c0aea294251203c00a2639 Mon Sep 17 00:00:00 2001
From: Nils Henrik Hals <3185998+Strepto@users.noreply.github.com>
Date: Wed, 11 Mar 2026 14:17:58 +0100
Subject: [PATCH 07/11] Apply suggestions from code review
Co-authored-by: Martin Costello
---
docs/configure-and-customize-redoc.md | 4 ++--
docs/configure-and-customize-swaggerui.md | 3 ++-
.../SwaggerUIBuilderExtensions.cs | 4 ++--
test/WebSites/WebApi.Map/Program.cs | 13 ++++++-------
4 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/docs/configure-and-customize-redoc.md b/docs/configure-and-customize-redoc.md
index 718d515428..d74c195b64 100644
--- a/docs/configure-and-customize-redoc.md
+++ b/docs/configure-and-customize-redoc.md
@@ -136,7 +136,6 @@ app.UseReDoc(options =>
[redoc-options]: https://github.com/Redocly/redoc/blob/main/docs/deployment/html.md#the-redoc-object
-
## Use `MapReDoc` with endpoint routing
`MapReDoc` is an endpoint-routing alternative to `UseReDoc`. The options API is the same, so all customization examples on this page also apply when using `MapReDoc`.
@@ -156,7 +155,8 @@ The main differences are:
- **Use**ReDoc adds middleware directly to the request pipeline and returns `IApplicationBuilder`.
- **Map**ReDoc maps ReDoc via endpoint routing and returns `IEndpointConventionBuilder`.
-- Because `MapReDoc` returns an endpoint convention builder, you can attach endpoint metadata/conventions (for example, authorization policies) directly to the mapped ReDoc endpoint.
+- Because `MapReDoc` returns an endpoint convention builder, you can attach endpoint metadata/conventions (for example, authorization policies) directly to the mapped ReDoc endpoint.
+
diff --git a/docs/configure-and-customize-swaggerui.md b/docs/configure-and-customize-swaggerui.md
index 4501c20ba7..6155ffc2ca 100644
--- a/docs/configure-and-customize-swaggerui.md
+++ b/docs/configure-and-customize-swaggerui.md
@@ -265,7 +265,8 @@ The main differences are:
- **Use**SwaggerUI adds middleware directly to the request pipeline and returns `IApplicationBuilder`.
- **Map**SwaggerUI maps the Swagger UI via endpoint routing and returns `IEndpointConventionBuilder`.
-- Because `MapSwaggerUI` returns an endpoint convention builder, you can attach endpoint metadata/conventions (for example, authorization policies) directly to the mapped UI endpoint.
+- Because `MapSwaggerUI` returns an endpoint convention builder, you can attach endpoint metadata/conventions (for example, authorization policies) directly to the mapped UI endpoint.
+
diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIBuilderExtensions.cs b/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIBuilderExtensions.cs
index 69c60ca6d3..5ae656cdb0 100644
--- a/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIBuilderExtensions.cs
+++ b/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIBuilderExtensions.cs
@@ -34,10 +34,10 @@ public static IApplicationBuilder UseSwaggerUI(
///
///
/// This is a convenience extension method that combines the registration of the SwaggerUI middleware with endpoint
- /// routing. It allows you to specify the route pattern for the SwaggerUI and optionally configure the SwaggerUIOptions.
+ /// routing. It allows you to specify the route pattern for the SwaggerUI and optionally configure the .
///
/// Endpoint route builder to which the SwaggerUI middleware will be mapped.
- /// Optional setup action to configure the SwaggerUIOptions.
+ /// Optional setup action to configure the .
/// An that can be used to further configure the endpoint.
public static IEndpointConventionBuilder MapSwaggerUI(
this IEndpointRouteBuilder endpoints,
diff --git a/test/WebSites/WebApi.Map/Program.cs b/test/WebSites/WebApi.Map/Program.cs
index 0eefea33ae..918ee81175 100644
--- a/test/WebSites/WebApi.Map/Program.cs
+++ b/test/WebSites/WebApi.Map/Program.cs
@@ -5,8 +5,7 @@
var builder = WebApplication.CreateBuilder(args);
-builder.Services.Configure(options => options.SerializerOptions.Converters.Add(new JsonStringEnumConverter())
-);
+builder.Services.Configure(options => options.SerializerOptions.Converters.Add(new JsonStringEnumConverter()));
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options => { options.SwaggerDoc("v1", new() { Title = "WebApi", Version = "v1" }); });
@@ -21,12 +20,12 @@
// begin-snippet: SwaggerUI-MapSwaggerUI
app.MapSwagger();
-app.MapSwaggerUI(options => { options.SwaggerEndpoint("/swagger/v1/swagger.json", "V1 Docs"); });
+app.MapSwaggerUI(options => options.SwaggerEndpoint("/swagger/v1/swagger.json", "V1 Docs"));
// end-snippet
// begin-snippet: SwaggerUI-MapSwaggerUI-RequireAuthorization
-app.MapSwaggerUI(o => { o.RoutePrefix = "swagger-auth"; })
- .RequireAuthorization(); // Remember to also add RequireAuthorization to MapSwagger.
+app.MapSwaggerUI(o => o.RoutePrefix = "swagger-auth")
+ .RequireAuthorization(); // Remember to also add RequireAuthorization to MapSwagger.
// end-snippet
@@ -36,8 +35,8 @@
// end-snippet
// begin-snippet: Redoc-MapReDoc-RequireAuthorization
-app.MapReDoc(reDocOptions => { reDocOptions.RoutePrefix = "redoc-auth"; })
- .RequireAuthorization(); // Remember to also add RequireAuthorization to MapSwagger
+app.MapReDoc(reDocOptions => reDocOptions.RoutePrefix = "redoc-auth")
+ .RequireAuthorization(); // Remember to also add RequireAuthorization to MapSwagger
// end-snippet
app.UseHttpsRedirection();
From e31ed797ab24fa3864b5502fe7720b60cf2694e7 Mon Sep 17 00:00:00 2001
From: Nils Henrik Hals <3185998+Strepto@users.noreply.github.com>
Date: Wed, 11 Mar 2026 14:18:27 +0100
Subject: [PATCH 08/11] Separate tests to Swagger and ReDoc tests
---
.../ReDocIntegrationTests.cs | 27 +++++++++++++++++++
.../SwaggerAndSwaggerUIIntegrationTests.cs | 8 ------
2 files changed, 27 insertions(+), 8 deletions(-)
diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/ReDocIntegrationTests.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/ReDocIntegrationTests.cs
index 186665fdb2..9257dc805a 100644
--- a/test/Swashbuckle.AspNetCore.IntegrationTests/ReDocIntegrationTests.cs
+++ b/test/Swashbuckle.AspNetCore.IntegrationTests/ReDocIntegrationTests.cs
@@ -3,6 +3,7 @@
using System.Net.Http.Headers;
using System.Security.Cryptography;
using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Mvc.Testing;
using Swashbuckle.AspNetCore.ReDoc;
using ReDocApp = ReDoc;
@@ -53,6 +54,32 @@ static void AssertResource(HttpResponseMessage response)
}
}
+
+ [Theory]
+ [InlineData("/swagger/v1/swagger.json", "application/json")]
+ [InlineData("/swagger/v1/swagger.yaml", "text/yaml")]
+ [InlineData("/swagger/v1/swagger.yml", "text/yaml")]
+ [InlineData("/api-docs/index.html", "text/html")]
+ public async Task MapSwaggerAndMapReDoc_ReturnExpectedEndpoints(string path, string mediaType)
+ {
+ var client = new WebApplicationFactory().CreateClient();
+
+ var response = await client.GetAsync(path, TestContext.Current.CancellationToken);
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal(mediaType, response.Content.Headers.ContentType?.MediaType);
+ }
+
+ [Theory]
+ [InlineData("/redoc-auth/index.html")]
+ public async Task MapReDoc_RequireAuthorization_ReturnUnauthorized(string path)
+ {
+ var client = new WebApplicationFactory().CreateClient();
+ var response = await client.GetAsync(path, TestContext.Current.CancellationToken);
+ Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
+ }
+
+
[Fact]
public async Task RedocMiddleware_ReturnsInitializerScript()
{
diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerAndSwaggerUIIntegrationTests.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerAndSwaggerUIIntegrationTests.cs
index 8dd3c4e71b..1300fc25ea 100644
--- a/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerAndSwaggerUIIntegrationTests.cs
+++ b/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerAndSwaggerUIIntegrationTests.cs
@@ -21,15 +21,10 @@ public async Task SwaggerDocWithoutSubdirectory(string path, string mediaType)
}
[Theory]
- // MapSwagger()
[InlineData("/swagger/v1/swagger.json", "application/json")]
[InlineData("/swagger/v1/swagger.yaml", "text/yaml")]
[InlineData("/swagger/v1/swagger.yml", "text/yaml")]
- // MapSwaggerUI()
[InlineData("/swagger/index.html", "text/html")]
- // MapReDoc()
- [InlineData("/api-docs/index.html", "text/html")]
- [InlineData("/api-docs/index.js", "application/javascript")]
public async Task Map_Methods_ReturnExpectedEndpoints(string path, string mediaType)
{
var client = new WebApplicationFactory().CreateClient();
@@ -41,10 +36,7 @@ public async Task Map_Methods_ReturnExpectedEndpoints(string path, string mediaT
}
[Theory]
- // MapSwaggerUI().RequireAuthorization()
[InlineData("/swagger-auth/index.html")]
- // MapReDoc().RequireAuthorization()
- [InlineData("/redoc-auth/index.html")]
public async Task MapSwaggerUI_And_MapReDoc_RequireAuthorization_ReturnUnauthorized(string path)
{
var client = new WebApplicationFactory().CreateClient();
From 4672b49b07621ac91be994e2d4e0d5fe143bed77 Mon Sep 17 00:00:00 2001
From: Nils Henrik Hals <3185998+Strepto@users.noreply.github.com>
Date: Wed, 11 Mar 2026 14:55:11 +0100
Subject: [PATCH 09/11] Update mdsnippets
---
docs/configure-and-customize-redoc.md | 334 ++++++-------
docs/configure-and-customize-swaggerui.md | 554 +++++++++++-----------
2 files changed, 444 insertions(+), 444 deletions(-)
diff --git a/docs/configure-and-customize-redoc.md b/docs/configure-and-customize-redoc.md
index d74c195b64..eb9c67513d 100644
--- a/docs/configure-and-customize-redoc.md
+++ b/docs/configure-and-customize-redoc.md
@@ -1,169 +1,169 @@
-# Configuration and Customization of `Swashbuckle.AspNetCore.ReDoc`
-
-## Change Relative Path to the UI
-
-By default, the Redoc UI will be exposed at `/api-docs`. If necessary, you can alter this when enabling the Redoc middleware:
-
-
-
-
-```cs
-app.UseReDoc(options =>
-{
- options.RoutePrefix = "docs";
-});
-```
-snippet source | anchor
-
-
-
-## Change Document Title
-
-By default, the Redoc UI will have a generic document title. You can alter this when enabling the Redoc middleware:
-
-
-
-
-```cs
-app.UseReDoc(options =>
-{
- options.DocumentTitle = "My API Docs";
-});
-```
-snippet source | anchor
-
-
-
-## Apply Redoc Parameters
-
-Redoc ships with its own set of configuration parameters, all described [in the Redoc documentation][redoc-options].
-In Swashbuckle.AspNetCore, most of these are surfaced through the Redoc middleware options:
-
-
-
-
-```cs
-app.UseReDoc(options =>
-{
- options.SpecUrl("/v1/swagger.json");
- options.EnableUntrustedSpec();
- options.ScrollYOffset(10);
- options.HideHostname();
- options.HideDownloadButton();
- options.ExpandResponses("200,201");
- options.RequiredPropsFirst();
- options.NoAutoAuth();
- options.PathInMiddlePanel();
- options.HideLoading();
- options.NativeScrollbars();
- options.DisableSearch();
- options.OnlyRequiredInSamples();
- options.SortPropsAlphabetically();
-});
-```
-snippet source | anchor
-
-
-
-> [!NOTE]
-> Using `options.SpecUrl("/v1/swagger.json")` multiple times within the same `UseReDoc(...)` will not add multiple URLs.
-
-## Inject Custom CSS
-
-To tweak the look and feel, you can inject additional CSS stylesheets by adding them to your `wwwroot` folder and specifying
-the relative paths in the middleware options:
-
-
-
-
-```cs
-app.UseReDoc(options =>
-{
- options.InjectStylesheet("/redoc/custom.css");
-});
-```
-snippet source | anchor
-
-
-
-It is also possible to modify the theme by using the `AdditionalItems` property. More information can be found
-[in the Redoc documentation][redoc-options].
-
-
-
-
-```cs
-app.UseReDoc(options =>
-{
- options.ConfigObject.AdditionalItems = new Dictionary
- {
- // Configured additional options
- };
-});
-```
-snippet source | anchor
-
-
-
-## Customize index.html
-
-To customize the UI beyond the basic options listed above, you can provide your own version of the Redoc `index.html` page:
-
-
-
-
-```cs
-app.UseReDoc(options =>
-{
- options.IndexStream = () => typeof(Program).Assembly
- .GetManifestResourceStream("CustomIndex.ReDoc.index.html"); // Requires file to be added as an embedded resource
-});
-```
-snippet source | anchor
-
-
-
-```xml
-
-
-
-
-
-```
-
-> [!TIP]
-> To get started, you should base your custom `index.html` on the [default version](../src/Swashbuckle.AspNetCore.ReDoc/index.html).
-
-[redoc-options]: https://github.com/Redocly/redoc/blob/main/docs/deployment/html.md#the-redoc-object
-
-## Use `MapReDoc` with endpoint routing
-
-`MapReDoc` is an endpoint-routing alternative to `UseReDoc`. The options API is the same, so all customization examples on this page also apply when using `MapReDoc`.
-
-
-
-
-```cs
-app.MapReDoc();
-```
-snippet source | anchor
-
-
-
-The main differences are:
-
-- **Use**ReDoc adds middleware directly to the request pipeline and returns `IApplicationBuilder`.
-- **Map**ReDoc maps ReDoc via endpoint routing and returns `IEndpointConventionBuilder`.
-
+# Configuration and Customization of `Swashbuckle.AspNetCore.ReDoc`
+
+## Change Relative Path to the UI
+
+By default, the Redoc UI will be exposed at `/api-docs`. If necessary, you can alter this when enabling the Redoc middleware:
+
+
+
+
+```cs
+app.UseReDoc(options =>
+{
+ options.RoutePrefix = "docs";
+});
+```
+snippet source | anchor
+
+
+
+## Change Document Title
+
+By default, the Redoc UI will have a generic document title. You can alter this when enabling the Redoc middleware:
+
+
+
+
+```cs
+app.UseReDoc(options =>
+{
+ options.DocumentTitle = "My API Docs";
+});
+```
+snippet source | anchor
+
+
+
+## Apply Redoc Parameters
+
+Redoc ships with its own set of configuration parameters, all described [in the Redoc documentation][redoc-options].
+In Swashbuckle.AspNetCore, most of these are surfaced through the Redoc middleware options:
+
+
+
+
+```cs
+app.UseReDoc(options =>
+{
+ options.SpecUrl("/v1/swagger.json");
+ options.EnableUntrustedSpec();
+ options.ScrollYOffset(10);
+ options.HideHostname();
+ options.HideDownloadButton();
+ options.ExpandResponses("200,201");
+ options.RequiredPropsFirst();
+ options.NoAutoAuth();
+ options.PathInMiddlePanel();
+ options.HideLoading();
+ options.NativeScrollbars();
+ options.DisableSearch();
+ options.OnlyRequiredInSamples();
+ options.SortPropsAlphabetically();
+});
+```
+snippet source | anchor
+
+
+
+> [!NOTE]
+> Using `options.SpecUrl("/v1/swagger.json")` multiple times within the same `UseReDoc(...)` will not add multiple URLs.
+
+## Inject Custom CSS
+
+To tweak the look and feel, you can inject additional CSS stylesheets by adding them to your `wwwroot` folder and specifying
+the relative paths in the middleware options:
+
+
+
+
+```cs
+app.UseReDoc(options =>
+{
+ options.InjectStylesheet("/redoc/custom.css");
+});
+```
+snippet source | anchor
+
+
+
+It is also possible to modify the theme by using the `AdditionalItems` property. More information can be found
+[in the Redoc documentation][redoc-options].
+
+
+
+
+```cs
+app.UseReDoc(options =>
+{
+ options.ConfigObject.AdditionalItems = new Dictionary
+ {
+ // Configured additional options
+ };
+});
+```
+snippet source | anchor
+
+
+
+## Customize index.html
+
+To customize the UI beyond the basic options listed above, you can provide your own version of the Redoc `index.html` page:
+
+
+
+
+```cs
+app.UseReDoc(options =>
+{
+ options.IndexStream = () => typeof(Program).Assembly
+ .GetManifestResourceStream("CustomIndex.ReDoc.index.html"); // Requires file to be added as an embedded resource
+});
+```
+snippet source | anchor
+
+
+
+```xml
+
+
+
+
+
+```
+
+> [!TIP]
+> To get started, you should base your custom `index.html` on the [default version](../src/Swashbuckle.AspNetCore.ReDoc/index.html).
+
+[redoc-options]: https://github.com/Redocly/redoc/blob/main/docs/deployment/html.md#the-redoc-object
+
+## Use `MapReDoc` with endpoint routing
+
+`MapReDoc` is an endpoint-routing alternative to `UseReDoc`. The options API is the same, so all customization examples on this page also apply when using `MapReDoc`.
+
+
+
+
+```cs
+app.MapReDoc();
+```
+snippet source | anchor
+
+
+
+The main differences are:
+
+- **Use**ReDoc adds middleware directly to the request pipeline and returns `IApplicationBuilder`.
+- **Map**ReDoc maps ReDoc via endpoint routing and returns `IEndpointConventionBuilder`.
+
- Because `MapReDoc` returns an endpoint convention builder, you can attach endpoint metadata/conventions (for example, authorization policies) directly to the mapped ReDoc endpoint.
-
-
-
-```cs
-app.MapReDoc(reDocOptions => { reDocOptions.RoutePrefix = "redoc-auth"; })
- .RequireAuthorization(); // Remember to also add RequireAuthorization to MapSwagger
-```
-snippet source | anchor
-
-
+
+
+
+```cs
+app.MapReDoc(reDocOptions => reDocOptions.RoutePrefix = "redoc-auth")
+ .RequireAuthorization(); // Remember to also add RequireAuthorization to MapSwagger
+```
+snippet source | anchor
+
+
diff --git a/docs/configure-and-customize-swaggerui.md b/docs/configure-and-customize-swaggerui.md
index 6155ffc2ca..0ea12a533e 100644
--- a/docs/configure-and-customize-swaggerui.md
+++ b/docs/configure-and-customize-swaggerui.md
@@ -1,279 +1,279 @@
-# Configuration and Customization of `Swashbuckle.AspNetCore.SwaggerUI`
-
-## Change Relative Path to the UI
-
-By default, the Swagger UI will be exposed at `/swagger`. If necessary, you can alter this when enabling the SwaggerUI middleware:
-
-
-
-
-```cs
-app.UseSwaggerUI(options =>
-{
- options.RoutePrefix = "api-docs";
-});
-```
-snippet source | anchor
-
-
-
-## Change Document Title
-
-By default, the Swagger UI will have a generic document title. When you have multiple OpenAPI documents open, it can be difficult to
-tell them apart. You can alter this when enabling the SwaggerUI middleware:
-
-
-
-
-```cs
-app.UseSwaggerUI(options =>
-{
- options.DocumentTitle = "My Swagger UI";
-});
-```
-snippet source | anchor
-
-
-
-## Change CSS or JS Paths
-
-By default, the Swagger UI includes default CSS and JavaScript, but if you wish to change the path or URL (for example to use a CDN)
-you can override the defaults as shown below:
-
-
-
-
-```cs
-app.UseSwaggerUI(options =>
-{
- options.StylesPath = "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.29.1/swagger-ui.min.css";
- options.ScriptBundlePath = "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.29.1/swagger-ui-bundle.min.js";
- options.ScriptPresetsPath = "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.29.1/swagger-ui-standalone-preset.min.js";
-});
-```
-snippet source | anchor
-
-
-
-## List Multiple OpenAPI Documents
-
-When enabling the middleware, you're required to specify one or more OpenAPI endpoints (fully qualified or relative to the UI page) to
-power the UI. If you provide multiple endpoints, they'll be listed in the top right corner of the page, allowing users to toggle between
-the different documents. For example, the following configuration could be used to document different versions of an API.
-
-
-
-
-```cs
-app.UseSwaggerUI(options =>
-{
- options.SwaggerEndpoint("/swagger/v1/swagger.json", "V1 Docs");
- options.SwaggerEndpoint("/swagger/v2/swagger.json", "V2 Docs");
-});
-```
-snippet source | anchor
-
-
-
-## Apply swagger-ui Parameters
-
-[swagger-ui][swagger-ui] ships with its own set of configuration parameters, all described
-[by the swagger-ui Configuration](https://github.com/swagger-api/swagger-ui/blob/HEAD/docs/usage/configuration.md#display).
-In Swashbuckle.AspNetCore, most of these are surfaced through the SwaggerUI middleware options:
-
-
-
-
-```cs
-app.UseSwaggerUI(options =>
-{
- options.DefaultModelExpandDepth(2);
- options.DefaultModelRendering(ModelRendering.Model);
- options.DefaultModelsExpandDepth(-1);
- options.DisplayOperationId();
- options.DisplayRequestDuration();
- options.DocExpansion(DocExpansion.None);
- options.EnableDeepLinking();
- options.EnableFilter();
- options.EnablePersistAuthorization();
- options.EnableTryItOutByDefault();
- options.MaxDisplayedTags(5);
- options.ShowExtensions();
- options.ShowCommonExtensions();
- options.EnableValidator();
- options.SupportedSubmitMethods(SubmitMethod.Get, SubmitMethod.Head);
- options.UseRequestInterceptor("(request) => { return request; }");
- options.UseResponseInterceptor("(response) => { return response; }");
-});
-```
-snippet source | anchor
-
-
-
-## Inject Custom JavaScript
-
-To tweak the behavior, you can inject additional JavaScript files by adding them to your `wwwroot` folder and specifying
-the relative paths in the middleware options:
-
-
-
-
-```cs
-app.UseSwaggerUI(options =>
-{
- options.InjectJavascript("/swagger-ui/custom.js");
-});
-```
-snippet source | anchor
-
-
-
-## Inject Custom CSS
-
-To tweak the look and feel, you can inject additional CSS stylesheets by adding them to your `wwwroot` folder and specifying the
-relative paths in the middleware options:
-
-
-
-
-```cs
-app.UseSwaggerUI(options =>
-{
- options.InjectStylesheet("/swagger-ui/custom.css");
-});
-```
-snippet source | anchor
-
-
-
-## Customize index.html
-
-To customize the UI beyond the basic options listed above, you can provide your own version of the swagger-ui `index.html` page:
-
-
-
-
-```cs
-app.UseSwaggerUI(options =>
-{
- options.IndexStream = () => typeof(Program).Assembly
- .GetManifestResourceStream("CustomUIIndex.Swagger.index.html"); // Requires file to be added as an embedded resource
-});
-```
-snippet source | anchor
-
-
-
-```xml
-
-
-
-
-
-```
-
-> [!TIP]
-> To get started, you should base your custom `index.html` on the [built-in version](../src/Swashbuckle.AspNetCore.SwaggerUI/index.html).
-
-## Enable OAuth2.0 Flows
-
-[swagger-ui][swagger-ui] has built-in support to participate in OAuth2.0 authorization flows. It interacts with authorization and/or token
-endpoints, as specified in the OpenAPI JSON, to obtain access tokens for subsequent API calls. See
-[Adding Security Definitions and Requirements](configure-and-customize-swaggergen.md#add-security-definitions-and-requirements) for
-an example of adding OAuth2.0 metadata to the generated Swagger.
-
-If your OpenAPI endpoint includes the appropriate security metadata, the UI interaction should be automatically enabled. However, you
-can further customize OAuth support in the UI with the following settings below. See the
-[Swagger-UI documentation](https://github.com/swagger-api/swagger-ui/blob/HEAD/docs/usage/oauth2.md) for more information.
-
-
-
-
-```cs
-app.UseSwaggerUI(options =>
-{
- options.OAuthClientId("test-id");
- options.OAuthClientSecret("test-secret");
- options.OAuthUsername("test-user");
- options.OAuthRealm("test-realm");
- options.OAuthAppName("test-app");
- options.OAuth2RedirectUrl("url");
- options.OAuthScopeSeparator(" ");
- options.OAuthScopes("scope1", "scope2");
- options.OAuthAdditionalQueryStringParams(new Dictionary { ["foo"] = "bar" });
- options.OAuthUseBasicAuthenticationWithAccessCodeGrant();
- options.OAuthUsePkce();
-});
-```
-snippet source | anchor
-
-
-
-## Use client-side request and response interceptors
-
-To use custom interceptors on requests and responses going through swagger-ui you can define them as JavaScript functions
-in the configuration:
-
-
-
-
-```cs
-app.UseSwaggerUI(options =>
-{
- options.UseRequestInterceptor("(req) => { req.headers['x-my-custom-header'] = 'MyCustomValue'; return req; }");
- options.UseResponseInterceptor("(res) => { console.log('Custom interceptor intercepted response from:', res.url); return res; }");
-});
-```
-snippet source | anchor
-
-
-
-This can be useful in a range of scenarios where you might want to append local XSRF tokens to all requests, for example:
-
-
-
-
-```cs
-app.UseSwaggerUI(options =>
-{
- options.UseRequestInterceptor("(req) => { req.headers['X-XSRF-Token'] = localStorage.getItem('xsrf-token'); return req; }");
-});
-```
-snippet source | anchor
-
-
-
-[swagger-ui]: https://github.com/swagger-api/swagger-ui
-
-## Use `MapSwaggerUI` with endpoint routing
-
-`MapSwaggerUI` is an endpoint-routing alternative to `UseSwaggerUI`. The options API is the same, so all customization examples on this page also apply when using `MapSwaggerUI`.
-
-
-
-
-```cs
-app.MapSwagger();
-app.MapSwaggerUI(options => { options.SwaggerEndpoint("/swagger/v1/swagger.json", "V1 Docs"); });
-```
-snippet source | anchor
-
-
-
-The main differences are:
-
-- **Use**SwaggerUI adds middleware directly to the request pipeline and returns `IApplicationBuilder`.
-- **Map**SwaggerUI maps the Swagger UI via endpoint routing and returns `IEndpointConventionBuilder`.
-
+# Configuration and Customization of `Swashbuckle.AspNetCore.SwaggerUI`
+
+## Change Relative Path to the UI
+
+By default, the Swagger UI will be exposed at `/swagger`. If necessary, you can alter this when enabling the SwaggerUI middleware:
+
+
+
+
+```cs
+app.UseSwaggerUI(options =>
+{
+ options.RoutePrefix = "api-docs";
+});
+```
+snippet source | anchor
+
+
+
+## Change Document Title
+
+By default, the Swagger UI will have a generic document title. When you have multiple OpenAPI documents open, it can be difficult to
+tell them apart. You can alter this when enabling the SwaggerUI middleware:
+
+
+
+
+```cs
+app.UseSwaggerUI(options =>
+{
+ options.DocumentTitle = "My Swagger UI";
+});
+```
+snippet source | anchor
+
+
+
+## Change CSS or JS Paths
+
+By default, the Swagger UI includes default CSS and JavaScript, but if you wish to change the path or URL (for example to use a CDN)
+you can override the defaults as shown below:
+
+
+
+
+```cs
+app.UseSwaggerUI(options =>
+{
+ options.StylesPath = "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.29.1/swagger-ui.min.css";
+ options.ScriptBundlePath = "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.29.1/swagger-ui-bundle.min.js";
+ options.ScriptPresetsPath = "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.29.1/swagger-ui-standalone-preset.min.js";
+});
+```
+snippet source | anchor
+
+
+
+## List Multiple OpenAPI Documents
+
+When enabling the middleware, you're required to specify one or more OpenAPI endpoints (fully qualified or relative to the UI page) to
+power the UI. If you provide multiple endpoints, they'll be listed in the top right corner of the page, allowing users to toggle between
+the different documents. For example, the following configuration could be used to document different versions of an API.
+
+
+
+
+```cs
+app.UseSwaggerUI(options =>
+{
+ options.SwaggerEndpoint("/swagger/v1/swagger.json", "V1 Docs");
+ options.SwaggerEndpoint("/swagger/v2/swagger.json", "V2 Docs");
+});
+```
+snippet source | anchor
+
+
+
+## Apply swagger-ui Parameters
+
+[swagger-ui][swagger-ui] ships with its own set of configuration parameters, all described
+[by the swagger-ui Configuration](https://github.com/swagger-api/swagger-ui/blob/HEAD/docs/usage/configuration.md#display).
+In Swashbuckle.AspNetCore, most of these are surfaced through the SwaggerUI middleware options:
+
+
+
+
+```cs
+app.UseSwaggerUI(options =>
+{
+ options.DefaultModelExpandDepth(2);
+ options.DefaultModelRendering(ModelRendering.Model);
+ options.DefaultModelsExpandDepth(-1);
+ options.DisplayOperationId();
+ options.DisplayRequestDuration();
+ options.DocExpansion(DocExpansion.None);
+ options.EnableDeepLinking();
+ options.EnableFilter();
+ options.EnablePersistAuthorization();
+ options.EnableTryItOutByDefault();
+ options.MaxDisplayedTags(5);
+ options.ShowExtensions();
+ options.ShowCommonExtensions();
+ options.EnableValidator();
+ options.SupportedSubmitMethods(SubmitMethod.Get, SubmitMethod.Head);
+ options.UseRequestInterceptor("(request) => { return request; }");
+ options.UseResponseInterceptor("(response) => { return response; }");
+});
+```
+snippet source | anchor
+
+
+
+## Inject Custom JavaScript
+
+To tweak the behavior, you can inject additional JavaScript files by adding them to your `wwwroot` folder and specifying
+the relative paths in the middleware options:
+
+
+
+
+```cs
+app.UseSwaggerUI(options =>
+{
+ options.InjectJavascript("/swagger-ui/custom.js");
+});
+```
+snippet source | anchor
+
+
+
+## Inject Custom CSS
+
+To tweak the look and feel, you can inject additional CSS stylesheets by adding them to your `wwwroot` folder and specifying the
+relative paths in the middleware options:
+
+
+
+
+```cs
+app.UseSwaggerUI(options =>
+{
+ options.InjectStylesheet("/swagger-ui/custom.css");
+});
+```
+snippet source | anchor
+
+
+
+## Customize index.html
+
+To customize the UI beyond the basic options listed above, you can provide your own version of the swagger-ui `index.html` page:
+
+
+
+
+```cs
+app.UseSwaggerUI(options =>
+{
+ options.IndexStream = () => typeof(Program).Assembly
+ .GetManifestResourceStream("CustomUIIndex.Swagger.index.html"); // Requires file to be added as an embedded resource
+});
+```
+snippet source | anchor
+
+
+
+```xml
+
+
+
+
+
+```
+
+> [!TIP]
+> To get started, you should base your custom `index.html` on the [built-in version](../src/Swashbuckle.AspNetCore.SwaggerUI/index.html).
+
+## Enable OAuth2.0 Flows
+
+[swagger-ui][swagger-ui] has built-in support to participate in OAuth2.0 authorization flows. It interacts with authorization and/or token
+endpoints, as specified in the OpenAPI JSON, to obtain access tokens for subsequent API calls. See
+[Adding Security Definitions and Requirements](configure-and-customize-swaggergen.md#add-security-definitions-and-requirements) for
+an example of adding OAuth2.0 metadata to the generated Swagger.
+
+If your OpenAPI endpoint includes the appropriate security metadata, the UI interaction should be automatically enabled. However, you
+can further customize OAuth support in the UI with the following settings below. See the
+[Swagger-UI documentation](https://github.com/swagger-api/swagger-ui/blob/HEAD/docs/usage/oauth2.md) for more information.
+
+
+
+
+```cs
+app.UseSwaggerUI(options =>
+{
+ options.OAuthClientId("test-id");
+ options.OAuthClientSecret("test-secret");
+ options.OAuthUsername("test-user");
+ options.OAuthRealm("test-realm");
+ options.OAuthAppName("test-app");
+ options.OAuth2RedirectUrl("url");
+ options.OAuthScopeSeparator(" ");
+ options.OAuthScopes("scope1", "scope2");
+ options.OAuthAdditionalQueryStringParams(new Dictionary { ["foo"] = "bar" });
+ options.OAuthUseBasicAuthenticationWithAccessCodeGrant();
+ options.OAuthUsePkce();
+});
+```
+snippet source | anchor
+
+
+
+## Use client-side request and response interceptors
+
+To use custom interceptors on requests and responses going through swagger-ui you can define them as JavaScript functions
+in the configuration:
+
+
+
+
+```cs
+app.UseSwaggerUI(options =>
+{
+ options.UseRequestInterceptor("(req) => { req.headers['x-my-custom-header'] = 'MyCustomValue'; return req; }");
+ options.UseResponseInterceptor("(res) => { console.log('Custom interceptor intercepted response from:', res.url); return res; }");
+});
+```
+snippet source | anchor
+
+
+
+This can be useful in a range of scenarios where you might want to append local XSRF tokens to all requests, for example:
+
+
+
+
+```cs
+app.UseSwaggerUI(options =>
+{
+ options.UseRequestInterceptor("(req) => { req.headers['X-XSRF-Token'] = localStorage.getItem('xsrf-token'); return req; }");
+});
+```
+snippet source | anchor
+
+
+
+[swagger-ui]: https://github.com/swagger-api/swagger-ui
+
+## Use `MapSwaggerUI` with endpoint routing
+
+`MapSwaggerUI` is an endpoint-routing alternative to `UseSwaggerUI`. The options API is the same, so all customization examples on this page also apply when using `MapSwaggerUI`.
+
+
+
+
+```cs
+app.MapSwagger();
+app.MapSwaggerUI(options => options.SwaggerEndpoint("/swagger/v1/swagger.json", "V1 Docs"));
+```
+snippet source | anchor
+
+
+
+The main differences are:
+
+- **Use**SwaggerUI adds middleware directly to the request pipeline and returns `IApplicationBuilder`.
+- **Map**SwaggerUI maps the Swagger UI via endpoint routing and returns `IEndpointConventionBuilder`.
+
- Because `MapSwaggerUI` returns an endpoint convention builder, you can attach endpoint metadata/conventions (for example, authorization policies) directly to the mapped UI endpoint.
-
-
-
-```cs
-app.MapSwaggerUI(o => { o.RoutePrefix = "swagger-auth"; })
- .RequireAuthorization(); // Remember to also add RequireAuthorization to MapSwagger.
-```
-snippet source | anchor
-
-
+
+
+
+```cs
+app.MapSwaggerUI(o => o.RoutePrefix = "swagger-auth")
+ .RequireAuthorization(); // Remember to also add RequireAuthorization to MapSwagger.
+```
+snippet source | anchor
+
+
From f930566e4c7525be7de4c7a2502ab578f48e1bde Mon Sep 17 00:00:00 2001
From: Nils Henrik Hals <3185998+Strepto@users.noreply.github.com>
Date: Thu, 12 Mar 2026 14:12:42 +0100
Subject: [PATCH 10/11] Add routePrefix parameter to MapSwaggerUI and MapReDoc
---
docs/configure-and-customize-redoc.md | 2 +-
docs/configure-and-customize-swaggerui.md | 4 ++--
.../PublicAPI/PublicAPI.Unshipped.txt | 2 +-
.../ReDocBuilderExtensions.cs | 3 +++
.../PublicAPI/PublicAPI.Unshipped.txt | 2 +-
.../SwaggerUIBuilderExtensions.cs | 9 +++++++--
test/WebSites/WebApi.Map/Program.cs | 12 ++++++------
7 files changed, 21 insertions(+), 13 deletions(-)
diff --git a/docs/configure-and-customize-redoc.md b/docs/configure-and-customize-redoc.md
index eb9c67513d..965a125bb6 100644
--- a/docs/configure-and-customize-redoc.md
+++ b/docs/configure-and-customize-redoc.md
@@ -161,7 +161,7 @@ The main differences are:
```cs
-app.MapReDoc(reDocOptions => reDocOptions.RoutePrefix = "redoc-auth")
+app.MapReDoc("redoc-auth")
.RequireAuthorization(); // Remember to also add RequireAuthorization to MapSwagger
```
snippet source | anchor
diff --git a/docs/configure-and-customize-swaggerui.md b/docs/configure-and-customize-swaggerui.md
index 0ea12a533e..f248fd0a95 100644
--- a/docs/configure-and-customize-swaggerui.md
+++ b/docs/configure-and-customize-swaggerui.md
@@ -254,7 +254,7 @@ app.UseSwaggerUI(options =>
```cs
app.MapSwagger();
-app.MapSwaggerUI(options => options.SwaggerEndpoint("/swagger/v1/swagger.json", "V1 Docs"));
+app.MapSwaggerUI("swagger", options => options.SwaggerEndpoint("/swagger/v1/swagger.json", "V1 Docs"));
```
snippet source | anchor
@@ -271,7 +271,7 @@ The main differences are:
```cs
-app.MapSwaggerUI(o => o.RoutePrefix = "swagger-auth")
+app.MapSwaggerUI("swagger-auth")
.RequireAuthorization(); // Remember to also add RequireAuthorization to MapSwagger.
```
snippet source | anchor
diff --git a/src/Swashbuckle.AspNetCore.ReDoc/PublicAPI/PublicAPI.Unshipped.txt b/src/Swashbuckle.AspNetCore.ReDoc/PublicAPI/PublicAPI.Unshipped.txt
index 2263291395..d36ebb3ba0 100644
--- a/src/Swashbuckle.AspNetCore.ReDoc/PublicAPI/PublicAPI.Unshipped.txt
+++ b/src/Swashbuckle.AspNetCore.ReDoc/PublicAPI/PublicAPI.Unshipped.txt
@@ -1 +1 @@
-static Microsoft.AspNetCore.Builder.ReDocBuilderExtensions.MapReDoc(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, System.Action setupAction = null) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder
+static Microsoft.AspNetCore.Builder.ReDocBuilderExtensions.MapReDoc(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string routePrefix = null, System.Action setupAction = null) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder
diff --git a/src/Swashbuckle.AspNetCore.ReDoc/ReDocBuilderExtensions.cs b/src/Swashbuckle.AspNetCore.ReDoc/ReDocBuilderExtensions.cs
index 8d93ebbdc4..413bd94c97 100644
--- a/src/Swashbuckle.AspNetCore.ReDoc/ReDocBuilderExtensions.cs
+++ b/src/Swashbuckle.AspNetCore.ReDoc/ReDocBuilderExtensions.cs
@@ -32,13 +32,16 @@ public static IApplicationBuilder UseReDoc(
/// Maps the Redoc middleware to the specified endpoint route.
///
/// Endpoint route builder to which the Redoc middleware will be mapped.
+ /// Optional route prefix for the Redoc endpoint. If not provided, the value is used.
/// Optional setup action to configure the Redoc options.
/// An that can be used to further configure the endpoint.
public static IEndpointConventionBuilder MapReDoc(
this IEndpointRouteBuilder endpoints,
+ string routePrefix = null,
Action setupAction = null)
{
var options = ResolveOptions(endpoints.ServiceProvider, setupAction);
+ if (routePrefix != null) options.RoutePrefix = routePrefix;
EnsureDefaultSpecUrl(options);
var pipeline = endpoints.CreateApplicationBuilder()
diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/PublicAPI/PublicAPI.Unshipped.txt b/src/Swashbuckle.AspNetCore.SwaggerUI/PublicAPI/PublicAPI.Unshipped.txt
index 09764248dd..4bc6a2889c 100644
--- a/src/Swashbuckle.AspNetCore.SwaggerUI/PublicAPI/PublicAPI.Unshipped.txt
+++ b/src/Swashbuckle.AspNetCore.SwaggerUI/PublicAPI/PublicAPI.Unshipped.txt
@@ -1 +1 @@
-static Microsoft.AspNetCore.Builder.SwaggerUIBuilderExtensions.MapSwaggerUI(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, System.Action setupAction = null) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder
+static Microsoft.AspNetCore.Builder.SwaggerUIBuilderExtensions.MapSwaggerUI(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string routePrefix = null, System.Action setupAction = null) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder
diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIBuilderExtensions.cs b/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIBuilderExtensions.cs
index 5ae656cdb0..06f75e5584 100644
--- a/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIBuilderExtensions.cs
+++ b/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIBuilderExtensions.cs
@@ -34,16 +34,21 @@ public static IApplicationBuilder UseSwaggerUI(
///
///
/// This is a convenience extension method that combines the registration of the SwaggerUI middleware with endpoint
- /// routing. It allows you to specify the route pattern for the SwaggerUI and optionally configure the .
+ /// routing. It allows you to specify the route pattern for the SwaggerUI and optionally configure the .
///
/// Endpoint route builder to which the SwaggerUI middleware will be mapped.
- /// Optional setup action to configure the .
+ /// Optional: The route to register SwaggerUI on. Defaults to the value.
+ /// Optional setup action to configure the .
/// An that can be used to further configure the endpoint.
public static IEndpointConventionBuilder MapSwaggerUI(
this IEndpointRouteBuilder endpoints,
+ string routePrefix = null,
Action setupAction = null)
{
var options = ResolveOptions(endpoints.ServiceProvider, setupAction);
+
+ if (routePrefix != null) options.RoutePrefix = routePrefix;
+
var hostingEnv = endpoints.ServiceProvider.GetRequiredService();
EnsureDefaultUrl(options, hostingEnv.ApplicationName);
diff --git a/test/WebSites/WebApi.Map/Program.cs b/test/WebSites/WebApi.Map/Program.cs
index 918ee81175..2d9d786c7e 100644
--- a/test/WebSites/WebApi.Map/Program.cs
+++ b/test/WebSites/WebApi.Map/Program.cs
@@ -5,7 +5,7 @@
var builder = WebApplication.CreateBuilder(args);
-builder.Services.Configure(options => options.SerializerOptions.Converters.Add(new JsonStringEnumConverter()));
+builder.Services.Configure(options => options.SerializerOptions.Converters.Add(new JsonStringEnumConverter()));
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options => { options.SwaggerDoc("v1", new() { Title = "WebApi", Version = "v1" }); });
@@ -20,12 +20,12 @@
// begin-snippet: SwaggerUI-MapSwaggerUI
app.MapSwagger();
-app.MapSwaggerUI(options => options.SwaggerEndpoint("/swagger/v1/swagger.json", "V1 Docs"));
+app.MapSwaggerUI("swagger", options => options.SwaggerEndpoint("/swagger/v1/swagger.json", "V1 Docs"));
// end-snippet
// begin-snippet: SwaggerUI-MapSwaggerUI-RequireAuthorization
-app.MapSwaggerUI(o => o.RoutePrefix = "swagger-auth")
- .RequireAuthorization(); // Remember to also add RequireAuthorization to MapSwagger.
+app.MapSwaggerUI("swagger-auth")
+ .RequireAuthorization(); // Remember to also add RequireAuthorization to MapSwagger.
// end-snippet
@@ -35,8 +35,8 @@
// end-snippet
// begin-snippet: Redoc-MapReDoc-RequireAuthorization
-app.MapReDoc(reDocOptions => reDocOptions.RoutePrefix = "redoc-auth")
- .RequireAuthorization(); // Remember to also add RequireAuthorization to MapSwagger
+app.MapReDoc("redoc-auth")
+ .RequireAuthorization(); // Remember to also add RequireAuthorization to MapSwagger
// end-snippet
app.UseHttpsRedirection();
From 391202302524b6547bd6fec12918a68064682c56 Mon Sep 17 00:00:00 2001
From: Nils Henrik Hals <3185998+Strepto@users.noreply.github.com>
Date: Wed, 25 Mar 2026 22:03:48 +0100
Subject: [PATCH 11/11] Apply suggestions from code review
Co-authored-by: Martin Costello
---
src/Swashbuckle.AspNetCore.ReDoc/ReDocBuilderExtensions.cs | 6 +++++-
.../SwaggerUIBuilderExtensions.cs | 5 ++++-
test/WebSites/WebApi.Map/Program.cs | 2 +-
3 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/src/Swashbuckle.AspNetCore.ReDoc/ReDocBuilderExtensions.cs b/src/Swashbuckle.AspNetCore.ReDoc/ReDocBuilderExtensions.cs
index 413bd94c97..7d6d894069 100644
--- a/src/Swashbuckle.AspNetCore.ReDoc/ReDocBuilderExtensions.cs
+++ b/src/Swashbuckle.AspNetCore.ReDoc/ReDocBuilderExtensions.cs
@@ -41,7 +41,11 @@ public static IEndpointConventionBuilder MapReDoc(
Action setupAction = null)
{
var options = ResolveOptions(endpoints.ServiceProvider, setupAction);
- if (routePrefix != null) options.RoutePrefix = routePrefix;
+
+ if (routePrefix != null)
+ {
+ options.RoutePrefix = routePrefix;
+ }
EnsureDefaultSpecUrl(options);
var pipeline = endpoints.CreateApplicationBuilder()
diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIBuilderExtensions.cs b/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIBuilderExtensions.cs
index 06f75e5584..044297c18a 100644
--- a/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIBuilderExtensions.cs
+++ b/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIBuilderExtensions.cs
@@ -47,7 +47,10 @@ public static IEndpointConventionBuilder MapSwaggerUI(
{
var options = ResolveOptions(endpoints.ServiceProvider, setupAction);
- if (routePrefix != null) options.RoutePrefix = routePrefix;
+ if (routePrefix != null)
+ {
+ options.RoutePrefix = routePrefix;
+ }
var hostingEnv = endpoints.ServiceProvider.GetRequiredService();
EnsureDefaultUrl(options, hostingEnv.ApplicationName);
diff --git a/test/WebSites/WebApi.Map/Program.cs b/test/WebSites/WebApi.Map/Program.cs
index 2d9d786c7e..de2c440824 100644
--- a/test/WebSites/WebApi.Map/Program.cs
+++ b/test/WebSites/WebApi.Map/Program.cs
@@ -7,7 +7,7 @@
builder.Services.Configure(options => options.SerializerOptions.Converters.Add(new JsonStringEnumConverter()));
builder.Services.AddEndpointsApiExplorer();
-builder.Services.AddSwaggerGen(options => { options.SwaggerDoc("v1", new() { Title = "WebApi", Version = "v1" }); });
+builder.Services.AddSwaggerGen(options => options.SwaggerDoc("v1", new() { Title = "WebApi", Version = "v1" }));
// Authentication and Authorization are added to verify that MapSwaggerUI and MapReDoc work as expected when RequireAuthorization is used.
builder.Services.AddAuthentication().AddBearerToken();