From 7ccca7ebda0dc232ce81d83a2dfb255541696a34 Mon Sep 17 00:00:00 2001 From: desjoerd Date: Tue, 26 Sep 2023 08:31:20 +0200 Subject: [PATCH 1/2] Add rudimentary way to exclude all IHostedService implementations when running the CLI --- Swashbuckle.AspNetCore.sln | 15 ++++++++ .../HostingApplication.cs | 9 +++++ .../Swashbuckle.AspNetCore.Cli.Test.csproj | 1 + .../ToolTests.cs | 23 +++++++++++++ .../MinimalAppWithHostedService.csproj | 17 ++++++++++ .../MinimalAppWithHostedService/Program.cs | 34 +++++++++++++++++++ .../Properties/launchSettings.json | 13 +++++++ 7 files changed, 112 insertions(+) create mode 100644 test/WebSites/MinimalAppWithHostedService/MinimalAppWithHostedService.csproj create mode 100644 test/WebSites/MinimalAppWithHostedService/Program.cs create mode 100644 test/WebSites/MinimalAppWithHostedService/Properties/launchSettings.json diff --git a/Swashbuckle.AspNetCore.sln b/Swashbuckle.AspNetCore.sln index 083033fadb..edd0db5a2a 100644 --- a/Swashbuckle.AspNetCore.sln +++ b/Swashbuckle.AspNetCore.sln @@ -92,6 +92,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Swashbuckle.AspNetCore.Test EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MinimalApp", "test\WebSites\MinimalApp\MinimalApp.csproj", "{3D0126CB-5439-483C-B2D5-4B4BE111D15C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MinimalAppWithHostedService", "test\WebSites\MinimalAppWithHostedService\MinimalAppWithHostedService.csproj", "{DD2A4D91-C071-4767-A4BE-7F3772DA134F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -486,6 +488,18 @@ Global {3D0126CB-5439-483C-B2D5-4B4BE111D15C}.Release|x64.Build.0 = Release|Any CPU {3D0126CB-5439-483C-B2D5-4B4BE111D15C}.Release|x86.ActiveCfg = Release|Any CPU {3D0126CB-5439-483C-B2D5-4B4BE111D15C}.Release|x86.Build.0 = Release|Any CPU + {DD2A4D91-C071-4767-A4BE-7F3772DA134F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DD2A4D91-C071-4767-A4BE-7F3772DA134F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DD2A4D91-C071-4767-A4BE-7F3772DA134F}.Debug|x64.ActiveCfg = Debug|Any CPU + {DD2A4D91-C071-4767-A4BE-7F3772DA134F}.Debug|x64.Build.0 = Debug|Any CPU + {DD2A4D91-C071-4767-A4BE-7F3772DA134F}.Debug|x86.ActiveCfg = Debug|Any CPU + {DD2A4D91-C071-4767-A4BE-7F3772DA134F}.Debug|x86.Build.0 = Debug|Any CPU + {DD2A4D91-C071-4767-A4BE-7F3772DA134F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DD2A4D91-C071-4767-A4BE-7F3772DA134F}.Release|Any CPU.Build.0 = Release|Any CPU + {DD2A4D91-C071-4767-A4BE-7F3772DA134F}.Release|x64.ActiveCfg = Release|Any CPU + {DD2A4D91-C071-4767-A4BE-7F3772DA134F}.Release|x64.Build.0 = Release|Any CPU + {DD2A4D91-C071-4767-A4BE-7F3772DA134F}.Release|x86.ActiveCfg = Release|Any CPU + {DD2A4D91-C071-4767-A4BE-7F3772DA134F}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -524,6 +538,7 @@ Global {76692D68-C38C-4A7D-B3DA-DA78A393E266} = {0ADCB223-F375-45AB-8BC4-834EC9C69554} {66590FBA-5FDD-4AC9-AF91-26ADAB33CCB8} = {0ADCB223-F375-45AB-8BC4-834EC9C69554} {3D0126CB-5439-483C-B2D5-4B4BE111D15C} = {DB3F57FC-1472-4F03-B551-43394DA3C5EB} + {DD2A4D91-C071-4767-A4BE-7F3772DA134F} = {DB3F57FC-1472-4F03-B551-43394DA3C5EB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {36FC6A67-247D-4149-8EDD-79FFD1A75F51} diff --git a/src/Swashbuckle.AspNetCore.Cli/HostingApplication.cs b/src/Swashbuckle.AspNetCore.Cli/HostingApplication.cs index f17c6c1419..e7ddcac4b8 100644 --- a/src/Swashbuckle.AspNetCore.Cli/HostingApplication.cs +++ b/src/Swashbuckle.AspNetCore.Cli/HostingApplication.cs @@ -31,6 +31,15 @@ void ConfigureHostBuilder(object hostBuilder) { services.AddSingleton(); services.AddSingleton(); + + for (var i = services.Count - 1; i >= 0; i--) + { + if (typeof(IHostedService).IsAssignableFrom(services[i].ServiceType) + && services[i].ImplementationType is not { FullName: "Microsoft.AspNetCore.Hosting.GenericWebHostService" }) + { + services.RemoveAt(i); + } + } }); } diff --git a/test/Swashbuckle.AspNetCore.Cli.Test/Swashbuckle.AspNetCore.Cli.Test.csproj b/test/Swashbuckle.AspNetCore.Cli.Test/Swashbuckle.AspNetCore.Cli.Test.csproj index 32a8170152..e57a6e57e4 100644 --- a/test/Swashbuckle.AspNetCore.Cli.Test/Swashbuckle.AspNetCore.Cli.Test.csproj +++ b/test/Swashbuckle.AspNetCore.Cli.Test/Swashbuckle.AspNetCore.Cli.Test.csproj @@ -6,6 +6,7 @@ + diff --git a/test/Swashbuckle.AspNetCore.Cli.Test/ToolTests.cs b/test/Swashbuckle.AspNetCore.Cli.Test/ToolTests.cs index dba0a076e1..8f8bf245aa 100644 --- a/test/Swashbuckle.AspNetCore.Cli.Test/ToolTests.cs +++ b/test/Swashbuckle.AspNetCore.Cli.Test/ToolTests.cs @@ -57,6 +57,29 @@ public void Can_Generate_Swagger_Json_ForTopLevelApp() dir.Delete(true); } } + + [Fact] + public void Does_Not_Run_Crashing_HostedService() + { + var dir = Directory.CreateDirectory(Path.Join(Path.GetTempPath(), Path.GetRandomFileName())); + try + { + var args = new string[] { "tofile", "--output", $"{dir}/swagger.json", Path.Combine(Directory.GetCurrentDirectory(), "MinimalAppWithHostedService.dll"), "v1" }; + + Assert.Equal(0, Program.Main(args)); + + using var document = JsonDocument.Parse(File.ReadAllText(Path.Combine(dir.FullName, "swagger.json"))); + + // verify one of the endpoints + var paths = document.RootElement.GetProperty("paths"); + var path = paths.GetProperty("/ShouldContain"); + Assert.True(path.TryGetProperty("get", out _)); + } + finally + { + dir.Delete(true); + } + } #endif } } diff --git a/test/WebSites/MinimalAppWithHostedService/MinimalAppWithHostedService.csproj b/test/WebSites/MinimalAppWithHostedService/MinimalAppWithHostedService.csproj new file mode 100644 index 0000000000..6e89e08bf8 --- /dev/null +++ b/test/WebSites/MinimalAppWithHostedService/MinimalAppWithHostedService.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + enable + enable + $([System.IO.Path]::Combine("..", "..", "..", "src", "Swashbuckle.AspNetCore.Cli", "bin", $(Configuration), $(TargetFramework), "dotnet-swagger")) + + + + + + + + + + diff --git a/test/WebSites/MinimalAppWithHostedService/Program.cs b/test/WebSites/MinimalAppWithHostedService/Program.cs new file mode 100644 index 0000000000..ece277cdcc --- /dev/null +++ b/test/WebSites/MinimalAppWithHostedService/Program.cs @@ -0,0 +1,34 @@ +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(c => +{ + c.SwaggerDoc("v1", new() { Title = "MinimalApp", Version = "v1" }); +}); + +builder.Services.AddHostedService(); + +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "MinimalApp v1")); +} + +app.MapGet("/ShouldContain", () => "Hello World!"); + +app.Run(); + +class HostedService : IHostedService +{ + public Task StartAsync(CancellationToken cancellationToken) + { + throw new Exception("Crash!"); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/test/WebSites/MinimalAppWithHostedService/Properties/launchSettings.json b/test/WebSites/MinimalAppWithHostedService/Properties/launchSettings.json new file mode 100644 index 0000000000..9502f1b305 --- /dev/null +++ b/test/WebSites/MinimalAppWithHostedService/Properties/launchSettings.json @@ -0,0 +1,13 @@ +{ + "profiles": { + "MinimalAppWithHostedService": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7128;http://localhost:5201", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} From c2605823fa45d102da6f59c75db6f2b869a18d85 Mon Sep 17 00:00:00 2001 From: desjoerd Date: Tue, 26 Sep 2023 08:35:22 +0200 Subject: [PATCH 2/2] Add comment explaining filtering of IHostedService implementations --- src/Swashbuckle.AspNetCore.Cli/HostingApplication.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Swashbuckle.AspNetCore.Cli/HostingApplication.cs b/src/Swashbuckle.AspNetCore.Cli/HostingApplication.cs index e7ddcac4b8..26ac200eca 100644 --- a/src/Swashbuckle.AspNetCore.Cli/HostingApplication.cs +++ b/src/Swashbuckle.AspNetCore.Cli/HostingApplication.cs @@ -34,6 +34,9 @@ void ConfigureHostBuilder(object hostBuilder) for (var i = services.Count - 1; i >= 0; i--) { + // exclude all implementations of IHostedService + // except Microsoft.AspNetCore.Hosting.GenericWebHostService because that one will build/configure + // the WebApplication/Middleware pipeline in the case of the GenericWebHostBuilder. if (typeof(IHostedService).IsAssignableFrom(services[i].ServiceType) && services[i].ImplementationType is not { FullName: "Microsoft.AspNetCore.Hosting.GenericWebHostService" }) {