Generate C# client for OpenAPI

What is OpenAPI

Generate client from file

dotnet new classlib --framework netstandard2.0 --output src/Sdks/PetStore --name Kaylumah.GenerateCSharpClientForOpenAPI.Sdks.PetStore
dotnet add package NSwag.MSBuild
dotnet add package System.ComponentModel.Annotations
dotnet add package Newtonsoft.Json
{
"runtime": "NetCore31",
"documentGenerator": {
"fromDocument": {
"json": "swagger.json"
}
},
"codeGenerators": {
"openApiToCSharpClient": {
"output": "Client.g.cs"
}
}
}
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="NSwag.MSBuild" Version="13.11.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
</ItemGroup>

<Target Name="GenerateSdk" BeforeTargets="Build">
<Exec Command="$(NSwagExe_Core31) run nswag.json " />
</Target>

</Project>
dotnet new console --framework netcoreapp3.1 --output src/Client/ApiClient --name Kaylumah.GenerateCSharpClientForOpenAPI.Client.ApiClient
using System;
using System.Net.Http;
using System.Threading.Tasks;

namespace Kaylumah.GenerateCSharpClientForOpenAPI.Client.ApiClient
{
class Program
{
static async Task Main(string[] args)
{
var httpClient = new HttpClient();
var apiClient = new MyNamespace.Client(httpClient);
var result = await apiClient.GetInventoryAsync();
Console.WriteLine(string.Join("|", result.Keys));
}
}
}

Influence created output

{
"runtime": "NetCore31",
"defaultVariables": "Configuration=Debug",
"documentGenerator": {
"fromDocument": {
"json": "$(InputDocument)"
}
},
"codeGenerators": {
"openApiToCSharpClient": {
"generateClientInterfaces": true,
"exceptionClass": "$(SdkName)ApiException",
"useBaseUrl": true,
"generateBaseUrlProperty": true,
"generateContractsOutput": true,
"contractsNamespace": "$(SdkNamespace).Interface",
"contractsOutputFilePath": "$(GeneratedContractFile)",
"className": "$(SdkName)Client",
"operationGenerationMode": "SingleClientFromOperationId",
"namespace": "$(SdkNamespace).Service",
"output": "$(GeneratedClientFile)"
}
}
}
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="NSwag.MSBuild" Version="13.11.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
</ItemGroup>

<Target Name="GenerateSdk" BeforeTargets="Build">
<PropertyGroup>
<OpenAPIDocument>swagger.json</OpenAPIDocument>
<NSwagConfiguration>nswag.json</NSwagConfiguration>

<SdkNamespace>$(RootNamespace)</SdkNamespace>
<SdkName>PetStore</SdkName>
<GeneratedInterfaceFile>$(SdkName).Interface.g.cs</GeneratedInterfaceFile>
<GeneratedServiceFile>$(SdkName).Service.g.cs</GeneratedServiceFile>

</PropertyGroup>
<Error Text="The OpenAPI document '$(OpenAPIDocument)' does not exists!" Condition="!Exists('$(OpenAPIDocument)')" />
<Error Text="The NSwag configuration '$(NSwagConfiguration)' does not exists!" Condition="!Exists('$(NSwagConfiguration)')" />
<Exec Command="$(NSwagExe_Core31) run $(NSwagConfiguration) /variables:Configuration=$(Configuration),InputDocument=$(OpenAPIDocument),SdkName=$(SdkName),SdkNamespace=$(SdkNamespace),GeneratedClientFile=$(GeneratedServiceFile),GeneratedContractFile=$(GeneratedInterfaceFile)" />
</Target>

</Project>

Generate client from API in your project

dotnet new classlib --framework netstandard2.0 --output src/Sdks/FromNswagApi --name Kaylumah.GenerateCSharpClientForOpenAPI.Sdks.FromNswagApi
dotnet add package NSwag.MSBuild
dotnet add package System.ComponentModel.Annotations
dotnet add package Newtonsoft.Json
dotnet new webapi --framework netcoreapp3.1 --output src/Apis/Nswag/WeatherForecastApi --name Kaylumah.GenerateCSharpClientForOpenAPI.Apis.Nswag.WeatherForecastApi
{
"runtime": "NetCore31",
"documentGenerator": {
"aspNetCoreToOpenApi": {
"project": "../../Apis/Nswag/WeatherForecastApi/Kaylumah.GenerateCSharpClientForOpenAPI.Apis.Nswag.WeatherForecastApi.csproj"
}
},
"codeGenerators": {
"openApiToCSharpClient": {
"generateClientInterfaces": true,
"exceptionClass": "$(SdkName)ApiException",
"useBaseUrl": true,
"generateBaseUrlProperty": true,
"generateContractsOutput": true,
"contractsNamespace": "$(SdkNamespace).Interface",
"contractsOutputFilePath": "$(GeneratedContractFile)",
"className": "$(SdkName)Client",
"operationGenerationMode": "SingleClientFromOperationId",
"namespace": "$(SdkNamespace).Service",
"output": "$(GeneratedClientFile)"
}
}
}
<Target Name="GenerateSdk" BeforeTargets="Build">
<PropertyGroup>
<NSwagConfiguration>nswag.json</NSwagConfiguration>

<SdkNamespace>$(RootNamespace)</SdkNamespace>
<SdkName>Weather</SdkName>
<GeneratedInterfaceFile>$(SdkName).Interface.g.cs</GeneratedInterfaceFile>
<GeneratedServiceFile>$(SdkName).Service.g.cs</GeneratedServiceFile>

</PropertyGroup>
<Error Text="The NSwag configuration '$(NSwagConfiguration)' does not exists!" Condition="!Exists('$(NSwagConfiguration)')" />
<Exec Command="$(NSwagExe_Core31) run $(NSwagConfiguration) /variables:Configuration=$(Configuration),SdkName=$(SdkName),SdkNamespace=$(SdkNamespace),GeneratedClientFile=$(GeneratedServiceFile),GeneratedContractFile=$(GeneratedInterfaceFile)" />
</Target>
Microsoft (R) Build Engine version 16.9.0+57a23d249 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.

Determining projects to restore...
All projects are up-to-date for restore.
NSwag command line tool for .NET Core NetCore31, toolchain v13.11.1.0 (NJsonSchema v10.4.3.0 (Newtonsoft.Json v12.0.0.0))
Visit http://NSwag.org for more information.
NSwag bin directory: /Users/maxhamulyak/.nuget/packages/nswag.msbuild/13.11.1/tools/NetCore31

Executing file 'nswag.json' with variables 'Configuration=Debug'...
Launcher directory: /Users/maxhamulyak/.nuget/packages/nswag.msbuild/13.11.1/tools/NetCore31
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.

Generate client from Swashbuckle project

dotnet new classlib --framework netstandard2.0 --output src/Sdks/FromSwashbuckleApi --name Kaylumah.GenerateCSharpClientForOpenAPI.Sdks.FromSwashbuckleApi
dotnet add package NSwag.MSBuild
dotnet add package System.ComponentModel.Annotations
dotnet add package Newtonsoft.Json
dotnet new webapi --framework netcoreapp3.1 --output src/Apis/Swashbuckle/WeatherForecastApi --name Kaylumah.GenerateCSharpClientForOpenAPI.Apis.Swashbuckle.WeatherForecastApi
dotnet new tool-manifest
dotnet tool install --version 6.1.4 Swashbuckle.AspNetCore.Cli
{
"sdk": {
"version": "3.1.406",
"rollForward": "latestPatch"
}
}
<Target Name="GenerateOpenAPI" BeforeTargets="GenerateSdk">
<Exec Command="dotnet swagger tofile --output swagger.json ../../Apis/Swashbuckle/WeatherForecastApi/bin/Debug/netcoreapp3.1/Kaylumah.GenerateCSharpClientForOpenAPI.Apis.Swashbuckle.WeatherForecastApi.dll v1" />
</Target>

<Target Name="GenerateSdk" BeforeTargets="Build">
<PropertyGroup>
<OpenAPIDocument>swagger.json</OpenAPIDocument>
<NSwagConfiguration>nswag.json</NSwagConfiguration>

<SdkNamespace>$(RootNamespace)</SdkNamespace>
<SdkName>Weather</SdkName>
<GeneratedInterfaceFile>$(SdkName).Interface.g.cs</GeneratedInterfaceFile>
<GeneratedServiceFile>$(SdkName).Service.g.cs</GeneratedServiceFile>
</PropertyGroup>
<Error Text="The OpenAPI document '$(OpenAPIDocument)' does not exists!" Condition="!Exists('$(OpenAPIDocument)')" />
<Error Text="The NSwag configuration '$(NSwagConfiguration)' does not exists!" Condition="!Exists('$(NSwagConfiguration)')" />
<Exec Command="$(NSwagExe_Core31) run $(NSwagConfiguration) /variables:Configuration=$(Configuration),InputDocument=$(OpenAPIDocument),SdkName=$(SdkName),SdkNamespace=$(SdkNamespace),GeneratedClientFile=$(GeneratedServiceFile),GeneratedContractFile=$(GeneratedInterfaceFile)" />
</Target>
// Swashbuckle
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.11.1.0 (NJsonSchema v10.4.3.0 (Newtonsoft.Json v12.0.0.0))")]
public partial interface IWeatherClient
{
/// <returns>Success</returns>
/// <exception cref="WeatherApiException">A server side error occurred.</exception>
System.Threading.Tasks.Task<System.Collections.Generic.ICollection<WeatherForecast>> WeatherForecastAsync();

/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>Success</returns>
/// <exception cref="WeatherApiException">A server side error occurred.</exception>
System.Threading.Tasks.Task<System.Collections.Generic.ICollection<WeatherForecast>> WeatherForecastAsync(System.Threading.CancellationToken cancellationToken);

}

// NSwag
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.11.1.0 (NJsonSchema v10.4.3.0 (Newtonsoft.Json v12.0.0.0))")]
public partial interface IWeatherClient
{
/// <exception cref="WeatherApiException">A server side error occurred.</exception>
System.Threading.Tasks.Task<System.Collections.Generic.ICollection<WeatherForecast>> WeatherForecast_GetAsync();

/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <exception cref="WeatherApiException">A server side error occurred.</exception>
System.Threading.Tasks.Task<System.Collections.Generic.ICollection<WeatherForecast>> WeatherForecast_GetAsync(System.Threading.CancellationToken cancellationToken);

}

Closing Thoughts

Sources

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store