NSwag - Auto generating code
The logic in GlobalDcc.Api.Client is largely created by NSwag. From the controller in GlobalDcc.Api.Server NSwag generates a swagger.json file and from this file it creates Autogenerated\NSwagGenerated.cs in GlobalDcc.Api.Client. Which contains (most) of the logic for calling Global DCC's API.
NSwag is also used to set up OpenAPI (formerly known as Swagger) for GlobalDcc.Api.Server (using the NSwag.AspNetCore NuGet package). But this page is concerned with the auto generation aspect of NSwag.
The build process
NSwag runs as a post build event for GlobalDcc.Api.Server. It only runs when building in debug, because NSwag assumes the output path is bin\Debug (this might no longer be the case, we have now specified output path for GlobalDcc.Api.Server, and now it should also work in Release in theory).
To use NSwag for auto generating code, we use the NuGet package NSwag.MSBuild. Since we only run NSwag for Debug, this package is only included for Debug.
The post build event looks as follows:
<Exec EnvironmentVariables="ASPNETCORE_ENVIRONMENT=Development" Command="$(NSwagExe_Net60) aspnetcore2openapi /project:$(ProjectPath) /noBuild:true /output:swagger.json" />
<Exec Command="$(NSwagExe_Net60) openapi2csclient /input:swagger.json /namespace:GlobalDcc.Api.Client /output:../GlobalDcc.Api.Client/AutoGenerated/NSwagGenerated.cs /ClientBaseClass:"Client, IClient" /UseHttpClientCreationMethod:true /InjectHttpClient:false /GenerateBaseUrlProperty:false /ClientClassAccessModifier:"public abstract" /ClassName:{controller}BaseClient /GenerateSyncMethods:true /GenerateDtoTypes:false /AdditionalNamespaceUsages:DccModel.Message /HttpClientType:GlobalDcc.Api.Client.HttpClientWrapper /DisposeHttpClient:false" />
The first Exec
creates swagger.json. To do this it first sets an environment
variable, because NSwag will only generate code if in Development. It then calls
the NSwag dll for .NET 7.0 with the following arguments:
Argument | What does it do? |
---|---|
aspnetcore2openapi | Tells NSwag to create an Open API file from an ASP.NET core app. |
/project:$(ProjectPath) | Tells NSwag that it is the current project (GlobalDcc.Api.Server) that should be converted. |
/noBuild:true | Do not build. I think this often gets ignored. |
/output:swagger.json | Sets the output file to swagger.json. |
The second Exec
tells NSwag to create an auto generated client from the just
created Open API file.
Argument | What does it do? |
---|---|
openapi2csclient | Tells NSwag that the job is to generate a C# client from an Open API file. |
/input:swagger.json | The input file is swagger.json. |
/namespace:GlobalDcc.Api.Client | The code gets generated in the namespace GlobalDcc.Api.Client . |
/output:../GlobalDcc.Api.Client/AutoGenerated/NSwagGenerated.cs | The auto generated code is placed in the file ../GlobalDcc.Api.Client/AutoGenerated/NSwagGenerated.cs. The path is relative to GlobalDcc.Api.Server.csproj. |
/ClientBaseClass:\"Client, IClient\" | Adds : Client, IClient to the generated clients class, so it extends Client and implements IClient . |
/UseHttpClientCreationMethod:true | Use the CreateHttpClientAsync method to get the client that sends the actual HTTP requests. This method is not auto generated. |
/InjectHttpClient:false | Tells NSwag not to inject an HttpClient into the auto generated client. |
/GenerateBaseUrlProperty:false | Tells NSwag not to generate the BaseUrl property (it is instead created in Client ). |
/ClientClassAccessModifier:"public abstract\" | Makes the auto generated client public abstract . It is set to abstract because we do not want the consumer to use the constructor created by NSwag, so instead we create an extension with a constructor we like. |
/ClassName:{controller}BaseClient | The name of the auto generated client should be the name of the controller from GlobalDcc.Api.Server minus "Controller" plus "BaseClient". E.g. Version0Controller becomes Version0BaseClient . |
/GenerateSyncMethods:true | Auto generate synchronous methods, instead of only generating async methods. |
/GenerateDtoTypes:false | Do not generate Data Transfer Objects. This is because we use the objects in DccModel. |
/AdditionalNamespaceUsages:DccModel.Message | Adds a using DccModel.Message; line to the auto generated client. |
/HttpClientType:GlobalDcc.Api.Client.HttpClientWrapper | Tells NSwag to use GlobalDcc.Api.Client.HttpClientWrapper as the type for the client sending the HTTP requests. |
/DisposeHttpClient:false | The auto generated client should not dispose the HttpClientWrapper (because we reuse it). |
What is not auto generated
Apart from the code generated by NSwag, there are the following classes in GlobalDcc.Api.Client:
Client
: A base class for implementing some things we want to use in the auto generated client.IClient
: An interface for the methods we want the auto generated client and any future auto generated clients to contain. I.e. if we make aVersion1Controller
in GlobalDcc.Api.Server, the auto generated client for this controller should also implement these methods. Please add methods to this, if adding a new endpoint to the controller(s) in GlobalDcc.Api.Server.HttpClientWrapper
: Wrapper forHttpClient
to make sure we handle DNS changes.Version0Client
: Extension of the auto generated clientVersion0BaseClient
which has the constructor we want.ProblemDetails
: This is actually originally auto generated and then copied. It is the only DTO we wanted NSwag to generate.
References
- NSwag on github. This also presents NSwag's README.md.
- NSwag tutorial from Microsoft Laern