diff --git a/DotSwashbuckle.AspNetCore.sln b/DotSwashbuckle.AspNetCore.sln index 9031a44..12b62f3 100644 --- a/DotSwashbuckle.AspNetCore.sln +++ b/DotSwashbuckle.AspNetCore.sln @@ -109,6 +109,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Redoc", "test\WebSites\Redo EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotSwashbuckle.AspNetCore.Redoc", "src\DotSwashbuckle.AspNetCore.Redoc\DotSwashbuckle.AspNetCore.Redoc.csproj", "{570CE160-C749-429C-A499-7126DC5B15F6}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MinimalAppWithHostedService", "test\WebSites\MinimalAppWithHostedService\MinimalAppWithHostedService.csproj", "{C760F65B-5214-4104-99B2-3671D93F6FE2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -587,6 +589,18 @@ Global {570CE160-C749-429C-A499-7126DC5B15F6}.Release|x64.Build.0 = Release|Any CPU {570CE160-C749-429C-A499-7126DC5B15F6}.Release|x86.ActiveCfg = Release|Any CPU {570CE160-C749-429C-A499-7126DC5B15F6}.Release|x86.Build.0 = Release|Any CPU + {C760F65B-5214-4104-99B2-3671D93F6FE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C760F65B-5214-4104-99B2-3671D93F6FE2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C760F65B-5214-4104-99B2-3671D93F6FE2}.Debug|x64.ActiveCfg = Debug|Any CPU + {C760F65B-5214-4104-99B2-3671D93F6FE2}.Debug|x64.Build.0 = Debug|Any CPU + {C760F65B-5214-4104-99B2-3671D93F6FE2}.Debug|x86.ActiveCfg = Debug|Any CPU + {C760F65B-5214-4104-99B2-3671D93F6FE2}.Debug|x86.Build.0 = Debug|Any CPU + {C760F65B-5214-4104-99B2-3671D93F6FE2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C760F65B-5214-4104-99B2-3671D93F6FE2}.Release|Any CPU.Build.0 = Release|Any CPU + {C760F65B-5214-4104-99B2-3671D93F6FE2}.Release|x64.ActiveCfg = Release|Any CPU + {C760F65B-5214-4104-99B2-3671D93F6FE2}.Release|x64.Build.0 = Release|Any CPU + {C760F65B-5214-4104-99B2-3671D93F6FE2}.Release|x86.ActiveCfg = Release|Any CPU + {C760F65B-5214-4104-99B2-3671D93F6FE2}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -632,6 +646,7 @@ Global {04DBAE85-EA69-4EA5-ABF0-B6BC03B0EB7E} = {DB3F57FC-1472-4F03-B551-43394DA3C5EB} {5AFB39E3-38A6-4061-A0FD-6DCD8A796750} = {DB3F57FC-1472-4F03-B551-43394DA3C5EB} {570CE160-C749-429C-A499-7126DC5B15F6} = {15A55F4A-FC33-4D96-BAAD-FBDCDD96D5F5} + {C760F65B-5214-4104-99B2-3671D93F6FE2} = {DB3F57FC-1472-4F03-B551-43394DA3C5EB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {36FC6A67-247D-4149-8EDD-79FFD1A75F51} diff --git a/src/DotSwashbuckle.AspNetCore.Cli/HostingApplication.cs b/src/DotSwashbuckle.AspNetCore.Cli/HostingApplication.cs index 40a2d49..6a30e46 100644 --- a/src/DotSwashbuckle.AspNetCore.Cli/HostingApplication.cs +++ b/src/DotSwashbuckle.AspNetCore.Cli/HostingApplication.cs @@ -28,6 +28,18 @@ void ConfigureHostBuilder(object hostBuilder) { services.AddSingleton(); services.AddSingleton(); + + 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" }) + { + services.RemoveAt(i); + } + } }); } diff --git a/test/DotSwashbuckle.AspNetCore.Cli.Test/DotSwashbuckle.AspNetCore.Cli.Test.csproj b/test/DotSwashbuckle.AspNetCore.Cli.Test/DotSwashbuckle.AspNetCore.Cli.Test.csproj index 04a9677..d33d51a 100644 --- a/test/DotSwashbuckle.AspNetCore.Cli.Test/DotSwashbuckle.AspNetCore.Cli.Test.csproj +++ b/test/DotSwashbuckle.AspNetCore.Cli.Test/DotSwashbuckle.AspNetCore.Cli.Test.csproj @@ -12,6 +12,7 @@ + diff --git a/test/DotSwashbuckle.AspNetCore.Cli.Test/ToolTests.cs b/test/DotSwashbuckle.AspNetCore.Cli.Test/ToolTests.cs index c94594d..05ebdd9 100644 --- a/test/DotSwashbuckle.AspNetCore.Cli.Test/ToolTests.cs +++ b/test/DotSwashbuckle.AspNetCore.Cli.Test/ToolTests.cs @@ -57,5 +57,28 @@ 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); + } + } } } diff --git a/test/WebSites/MinimalAppWithHostedService/MinimalAppWithHostedService.csproj b/test/WebSites/MinimalAppWithHostedService/MinimalAppWithHostedService.csproj new file mode 100644 index 0000000..6f7a639 --- /dev/null +++ b/test/WebSites/MinimalAppWithHostedService/MinimalAppWithHostedService.csproj @@ -0,0 +1,17 @@ + + + + net8.0 + enable + enable + $([System.IO.Path]::Combine("..", "..", "..", "src", "Swashbuckle.AspNetCore.Cli", "bin", $(Configuration), $(TargetFramework), "dotnet-swagger")) + + + + + + + + + + \ No newline at end of file diff --git a/test/WebSites/MinimalAppWithHostedService/Program.cs b/test/WebSites/MinimalAppWithHostedService/Program.cs new file mode 100644 index 0000000..ece277c --- /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 0000000..51422d0 --- /dev/null +++ b/test/WebSites/MinimalAppWithHostedService/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "MinimalAppWithHostedService": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:65032;http://localhost:65033" + } + } +} \ No newline at end of file diff --git a/test/WebSites/MinimalAppWithHostedService/launchsettings.json b/test/WebSites/MinimalAppWithHostedService/launchsettings.json new file mode 100644 index 0000000..4136201 --- /dev/null +++ b/test/WebSites/MinimalAppWithHostedService/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" + } + } + } +} \ No newline at end of file