NSwag
NSwag copied to clipboard
AspNetCoreToOpenApi. No service for type 'NSwag.Generation.IOpenApiDocumentGenerator' with .NET 8
Hi!
We use NSwag to generate TypeScript clients for our internal controller-based APIs. This works well with NSwag.MSBuild
13.20.0 and .NET 7.
During the .NET 8 upgrade, I have made the following changes:
- Upgrade to
NSwag.MSBuild
14.0.0-preview012. - Change to use Net80 as
runtime
in nswag.json. - Change the NSwag invocation to use
NSwagExe_Net80
.
When I build, I get:
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.InvalidOperationException: No service for type 'NSwag.Generation.IOpenApiDocumentGenerator' has been registered.
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at NSwag.Commands.Generation.AspNetCore.AspNetCoreToOpenApiCommand.GenerateDocumentWithDocumentProviderAsync(IServiceProvider serviceProvider) in /_/src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiCommand.cs:line 244
at NSwag.Commands.Generation.AspNetCore.AspNetCoreToOpenApiCommand.GenerateDocumentAsync(IServiceProvider serviceProvider, String currentWorkingDirectory) in /_/src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiCommand.cs:line 239
at NSwag.Commands.Generation.AspNetCore.AspNetCoreToOpenApiGeneratorCommandEntryPoint.Process(String commandContent, String outputFile, String applicationName) in /_/src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiGeneratorCommandEntryPoint.cs:line 29
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
--- End of inner exception stack trace ---
at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
at NSwag.AspNetCore.Launcher.Program.Main(String[] args) in /_/src/NSwag.AspNetCore.Launcher/Program.cs:line 132
System.InvalidOperationException: Swagger generation failed with non-zero exit code '1'.
at NSwag.Commands.Generation.AspNetCore.AspNetCoreToOpenApiCommand.RunAsync(CommandLineProcessor processor, IConsoleHost host) in /_/src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiCommand.cs:line 195
at NSwag.Commands.NSwagDocumentBase.GenerateSwaggerDocumentAsync() in /_/src/NSwag.Commands/NSwagDocumentBase.cs:line 270
at NSwag.Commands.NSwagDocument.ExecuteAsync() in /_/src/NSwag.Commands/NSwagDocument.cs:line 67
at NSwag.Commands.Document.ExecuteDocumentCommand.ExecuteDocumentAsync(IConsoleHost host, String filePath) in /_/src/NSwag.Commands/Commands/Document/ExecuteDocumentCommand.cs:line 76
at NSwag.Commands.Document.ExecuteDocumentCommand.RunAsync(CommandLineProcessor processor, IConsoleHost host) in /_/src/NSwag.Commands/Commands/Document/ExecuteDocumentCommand.cs:line 33
at NConsole.CommandLineProcessor.ProcessSingleAsync(String[] args, Object input)
at NConsole.CommandLineProcessor.ProcessAsync(String[] args, Object input)
at NSwag.Commands.NSwagCommandProcessor.ProcessAsync(String[] args) in /_/src/NSwag.Commands/NSwagCommandProcessor.cs:line 62
I include NSwag.MSBuild
like this:
<PackageReference Include="NSwag.MSBuild" Version="14.0.0-preview012">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
And I generate TS clients like this:
<Target Name="NSwag" BeforeTargets="AfterBuild">
<Exec ConsoleToMSBuild="true" ContinueOnError="true" Command="$(NSwagExe_Net80) run nswag.json /variables:Configuration=$(Configuration),TypescriptOutputPath=../../WebApp/app/generated">
<Output TaskParameter="ExitCode" PropertyName="NSwagExitCode" />
<Output TaskParameter="ConsoleOutput" PropertyName="NSwagOutput" />
</Exec>
<Message Text="$(NSwagOutput)" Condition="'$(NSwagExitCode)' == '0'" Importance="low" />
<Error Text="$(NSwagOutput)" Condition="'$(NSwagExitCode)' != '0'" />
</Target>
I have checked other issues such as https://github.com/RicoSuter/NSwag/issues/2387, but services.AddOpenApiDocument()
doesn't compile (presumably because of how I reference NSwag.MSBuild
), and it wasn't needed with .NET 7 anyway.
Did you find any solution @provegard ?
Update I am able to resolve this issue by adding AddOpenApiDocument in startup.cs file:
services.AddOpenApiDocument(configure =>
{
configure.Title = "Service ";
});
If you are not able to resolve this function then don't forget to add:
<PackageReference Include="NSwag.Annotations" Version="14.0.0" />
<PackageReference Include="NSwag.AspNetCore" Version="14.0.0" />
<PackageReference Include="NSwag.MSBuild" Version="14.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Since I don't want to generate an OpenAPI document (I already have that via another library), I added NSwag.Generation.AspNetCore
version 14.0.0-preview012
, then this:
services.AddSingleton<IOpenApiDocumentGenerator, GeneratorWrapper>();
...
private class GeneratorWrapper(IServiceProvider serviceProvider) : IOpenApiDocumentGenerator
{
public async Task<OpenApiDocument> GenerateAsync(string documentName)
{
var generator = new AspNetCoreOpenApiDocumentGenerator(new AspNetCoreOpenApiDocumentGeneratorSettings());
return await generator.GenerateAsync(serviceProvider);
}
}
@RicoSuter it is a breaking changes and problem
Hi! @RicoSuter
I have the same problems when updating from version .NET 7 to .NET 8 (NSwag.MSBuild and NSwag.AspNetCore 13.20.0 to 14.0.2). The same behavior is observed on .NET 7 if updating NSwag.MSBuild and NSwag.AspNetCore from version 13.20.0 to 14.0.2.
-
If I add the AddOpenApiDocument to the startup.cs file, then as a result of generating clients I get them for each controller, although in the aspNetCoreToOpenApi section in the apiGroupNames setting I have a group ["Sample"]. On version 13.20.0 everything worked without calling AddOpenApiDocument, and the generated clients corresponded to the "Sample" group. Attached is an example of the config apinswag.txt I understand that I can configure
AddOpenApiDocument(settings => settings.ApiGroupNames = new[] { "Sample" })
in this way, but in the final product I have many such nswag configs, and many client groups. I need this to be configured at the config level, and not at the code level, as was the case in version 13.20.0. -
If you use the ApiVersionNeutral attribute on the controller, generation fails with an error
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
12> ---> System.InvalidOperationException: The method 'get' on path '/api/application/InternalVersion' is registered multiple times.
12> at NSwag.Generation.AspNetCore.AspNetCoreOpenApiDocumentGenerator.AddOperationDescriptionsToDocument(OpenApiDocument document, Type controllerType, List`1 operations, OpenApiDocumentGenerator swaggerGenerator, OpenApiSchemaResolver schemaResolver)
12> at NSwag.Generation.AspNetCore.AspNetCoreOpenApiDocumentGenerator.GenerateApiGroups(OpenApiDocumentGenerator generator, OpenApiDocument document, IGrouping`2[] apiGroups, OpenApiSchemaResolver schemaResolver)
12> at NSwag.Generation.AspNetCore.AspNetCoreOpenApiDocumentGenerator.GenerateAsync(ApiDescriptionGroupCollection apiDescriptionGroups)
12> at NSwag.Commands.Generation.AspNetCore.AspNetCoreToOpenApiCommand.GenerateDocumentWithDocumentProviderAsync(IServiceProvider serviceProvider) in /_/src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiCommand.cs:line 245
12> at NSwag.Commands.Generation.AspNetCore.AspNetCoreToOpenApiCommand.GenerateDocumentAsync(IServiceProvider serviceProvider, String currentWorkingDirectory) in /_/src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiCommand.cs:line 239
12> at NSwag.Commands.Generation.AspNetCore.AspNetCoreToOpenApiGeneratorCommandEntryPoint.Process(String commandContent, String outputFile, String applicationName) in /_/src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiGeneratorCommandEntryPoint.cs:line 29
12> at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
12> at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
12> --- End of inner exception stack trace ---
12> at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
12> at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
12> at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
12> at NSwag.AspNetCore.Launcher.Program.Main(String[] args) in /_/src/NSwag.AspNetCore.Launcher/Program.cs:line 132
12>System.InvalidOperationException: Swagger generation failed with non-zero exit code '1'.
12> at NSwag.Commands.Generation.AspNetCore.AspNetCoreToOpenApiCommand.RunAsync(CommandLineProcessor processor, IConsoleHost host) in /_/src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiCommand.cs:line 195
12> at NSwag.Commands.NSwagDocumentBase.GenerateSwaggerDocumentAsync() in /_/src/NSwag.Commands/NSwagDocumentBase.cs:line 270
12> at NSwag.Commands.NSwagDocument.ExecuteAsync() in /_/src/NSwag.Commands/NSwagDocument.cs:line 67
12> at NSwag.Commands.Document.ExecuteDocumentCommand.ExecuteDocumentAsync(IConsoleHost host, String filePath) in /_/src/NSwag.Commands/Commands/Document/ExecuteDocumentCommand.cs:line 76
12> at NSwag.Commands.Document.ExecuteDocumentCommand.RunAsync(CommandLineProcessor processor, IConsoleHost host) in /_/src/NSwag.Commands/Commands/Document/ExecuteDocumentCommand.cs:line 33
12> at NConsole.CommandLineProcessor.ProcessSingleAsync(String[] args, Object input)
12> at NConsole.CommandLineProcessor.ProcessAsync(String[] args, Object input)
12> at NSwag.Commands.NSwagCommandProcessor.ProcessAsync(String[] args) in /_/src/NSwag.Commands/NSwagCommandProcessor.cs:line 62
Controller example
namespace DotNet.WebApi.Controllers
{
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[ApiVersionNeutral]
[Route("api/application/[controller]")]
public class InternalVersionController : ControllerBase
{
[HttpGet]
public Task Get() => Task.CompletedTask;
}
}
In the same project I have other controllers with controllers.txt With custom attributes and customized versioning. Again, this worked on version 13.20.0. I attach the generated swagger/index.html on version 13.20.0 Api_13.txt
How can I achieve the desired behavior without changing the code?
I have got the error:
<PackageReference Include="NSwag.MSBuild" Version="14.0.3" PrivateAssets="All" />
Since I don't want to generate an OpenAPI document (I already have that via another library), I added
NSwag.Generation.AspNetCore
version14.0.0-preview012
, then this:services.AddSingleton<IOpenApiDocumentGenerator, GeneratorWrapper>(); ... private class GeneratorWrapper(IServiceProvider serviceProvider) : IOpenApiDocumentGenerator { public async Task<OpenApiDocument> GenerateAsync(string documentName) { var generator = new AspNetCoreOpenApiDocumentGenerator(new AspNetCoreOpenApiDocumentGeneratorSettings()); return await generator.GenerateAsync(serviceProvider); } }
This solution worked for me. But because I used Swashbuckle.AspNetCore
for the Minimal API, I only want to use NSwag
to generate swagger.json
. In the past (.NET 6), I only need to ref to NSwag.MSBuild
, and run RunPostBuildEvent
to generate swagger file. I don't know if this behaviour of the new version of NSwag.MSBuild
is a bug.
Is this still not resolved? @RicoSuter
@priestlydevcounty the issue is open (see the status at the top) so you can expect it still to be... open.
@priestlydevcounty the issue is open (see the status at the top) so you can expect it still to be... open.
It was more of a question regarding any updates on the issue. It has been 6 months and several users have posted questions that have gone unanswered for a while. Looks like a serious breaking change to me.
Any message from the repo maintainer would be nice to know that he is aware of it and prioritising it accordingly. More worryingly, I read in another opened issue that this happens on every major version?
It was more of a question regarding any updates on the issue. It has been 6 months and several users have posted questions that have gone unanswered for a while. Looks like a serious breaking change to me.
The updates to this issue (and others) can be seen here in the GitHub issue thread, there's no secret society or forum that updates the status behind the scenes.