How to generate a WebAPI client for your ASP.NET Core 8.0 WebAPI: A Step-by-Step Guide with Swashbuckle and NSwag
ASP.NET Core WebAPIs are a great way to expose your business logic to the world. But how do you consume them? In this article, I’ll show you how to generate a WebAPI client for your ASP.NET Core WebAPI with Swashbuckle and NSwag.
Introduction
A good API is a well-documented API. But even with the best documentation, it can be hard to consume an API. Developers have to write a lot of boilerplate code to consume an API, including making HTTP requests, serializing and deserializing JSON, and handling errors. This can be time-consuming and error-prone.
It is important to provide a great developer experience. One way to do this is to provide a client library that developers can use to consume your API. ASP.NET Core WebAPI template already provides a way to show the API documentation using Swashbuckle. But it doesn’t provide a way to generate a client library. In this article, I’ll show you how to generate a WebAPI client for your ASP.NET Core WebAPI with Swashbuckle and NSwag, so that you can provide the client library to your consumers.
Swashbuckle is a library that automatically generates API documentation from your ASP.NET Core WebAPI. The default template for ASP.NET Core WebAPI includes Swashbuckle.
NSwag is similar to Swashbuckle. It does not only generate API documentation but also can generate client code for your API. It supports C# and TypeScript.
Note that NSwag provides a GUI application named NSwagStudio to generate the client library. However, it is not suitable for the CI/CD pipeline. In this article, we won’t use NSwagStudio. Instead, we’ll use the Swashbuckle CLI tool and the NSwag CLI tool to generate the client library in the build process, so that you can integrate it into your CI/CD pipeline.
Prerequisites
Before we start, make sure you have the following installed:
You can download the source code for this article from GitHub.
Creating an ASP.NET Core WebAPI
First, let’s create an ASP.NET Core WebAPI. Use VS 2022 to create a new project. Choose ASP.NET Core Web API template and click Create. Check the Enable OpenAPI support option to include Swashbuckle. Name the project WebApiClientDemo
.
The default WeatherForecast controller is good enough for our demo.
Check the Program.cs
file, and you should see the following code:
1 | builder.Services.AddSwaggerGen(); |
Run the application and navigate to https://localhost:<your port number>/swagger/index.html
. You should see the Swagger UI showing the API documentation.
Configuring the OpenAPI specification
We can add more information to the OpenAPI specification. So, developers can have a better understanding of the API. We can add the title, description, and contact information to the OpenAPI specification. We can also include the XML comments from the controllers in the OpenAPI document.
Update the
AddSwaggerGen()
method in theProgram.cs
file as follows:1
2
3
4
5
6
7
8
9
10
11
12
13
14builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Version = "v1",
Title = "My Web API",
Description = "An ASP.NET Core Web API",
Contact = new OpenApiContact
{
Name = "My Team",
Url = new Uri("https://myteam.com")
}
});
});Feel free to update the
OpenApiInfo
object with your own information.By default, the OpenAPI specification doesn’t contain the comments from the controllers. You can add the following code to the
WebApiDemo.csproj
file to include the comments from the controllers in the OpenAPI document:1
2
3
4<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>This code tells the compiler to generate an XML documentation file and to suppress the warning 1591, which is the warning for missing XML comments (
CS1591: Missing XML comment for publicly visible type or member
).Then, add the following code to the
AddSwaggerGen()
method:1
2var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));The preceding code tells Swashbuckle to include the XML comments in the OpenAPI document. So that you can see the comments in the Swagger UI. Add some comments to the WeatherForecast controller and the action methods.
So far, we have configured Swashbuckle to generate the OpenAPI specification with more information and include the XML comments from the controllers. When you run the application and navigate to https://localhost:<your_port_number>/swagger/index.html
, you should see the updated Swagger UI with more information.
You can see the title, description, and contact information in the Swagger UI. You can also see a link such as https://localhost:<your_port_number>/swagger/v1/swagger.json
. This is the URL to the OpenAPI specification.
Generating an OpenAPI specification file with Swashbuckle
Although we can see the OpenAPI specification in the Swagger UI, we need to generate a JSON file for NSwag to consume. Either Swashbuckle or NSwag can generate the OpenAPI specification file. However, Swashbuckle supports header parameters, while NSwag has some limitations. So, we’ll use Swashbuckle to generate the OpenAPI specification file, and then use NSwag to generate the client code.
Swashbuckkle provides a CLI tool to generate the OpenAPI specification file. You can use the following command to install the Swashbuckle CLI tool:
1
dotnet tool install -g Swashbuckle.AspNetCore.Cli
Then, you can run the following command to generate the OpenAPI specification file:
1
swagger tofile --output swagger-api.json bin\Debug\net8.0\WebApiClientDemo.dll v1
The preceding command has the following parameters:
- The
swagger tofile
command is used to generate the OpenAPI specification file. - The
--output
option is used to specify the output file. - The
bin\Debug\net8.0\WebApiClientDemo.dll
is the relative path to the Web API project. - The
v1
option is the name of the OpenAPI document, which is defined in theAddSwaggerGen()
method.
If the command is successful, you should see the
swagger-api.json
file in the project folder.- The
If you get an error like Unhandled exception. System.IO.FileNotFoundException: Could not load file or assembly 'Swashbuckle.AspNetCore.Swagger, Version=...'
, check the version of the Swashbuckle.AspNetCore package in the project. Make sure the version of the Swashbuckle CLI tool matches the version of the Swashbuckle.AspNetCore package. For example, in the sample project, the version of the Swashbuckle.AspNetCore package is 6.5.0, so the version of the Swashbuckle CLI tool should be 6.5.0. You can use the following command to install a specific version of the Swashbuckle CLI tool:
1 | dotnet tool install -g Swashbuckle.AspNetCore.Cli --version 6.5.0 |
Also, please make sure the path to the assembly is correct.
Of course, we don’t want to run the Swashbuckle CLI tool manually every time we update the WebAPI. We can add a post-build event to the WebAPI project to generate the OpenAPI specification file automatically.
To use the Swashbuckle CLI tool in the build process, run the following command to add a
dotnet-tools.json
file to the project:1
dotnet new tool-manifest
Then, you can run the following code to add the Swashbuckle CLI tool to the project:
1
dotnet tool install Swashbuckle.AspNetCore.Cli
The preceding command will update the
dotnet-tools.json
file and add the Swashbuckle CLI tool to the project. You can find thedotnet-tools.json
file in the project folder, as follows:1
2
3
4
5
6
7
8
9
10
11
12{
"version": 1,
"isRoot": true,
"tools": {
"swashbuckle.aspnetcore.cli": {
"version": "6.5.0",
"commands": [
"swagger"
]
}
}
}Next, update the
.csproj
file to include theswagger tofile
command in the build process. Add the following code to the.csproj
file:1
2
3
4<PropertyGroup>
<SolutionDirectory Condition=" '$(SolutionDirectory)' == '' ">$(MSBuildThisFileDirectory)..\</SolutionDirectory>
<ApiAssembly>$(SolutionDirectory)WebApiClientDemo\bin\$(Configuration)\net8.0\WebApiClientDemo.dll</ApiAssembly>
</PropertyGroup>The preceding code defines the
SolutionDirectory
andApiAssembly
properties. TheApiAssembly
property points to the Web API project. We will use it later. Please update the path to the Web API project if it is different from the sample project.Then, add the following code to the
.csproj
file:1
2
3
4<Target Name="BuildSwaggerFile" AfterTargets="Build">
<Exec EnvironmentVariables="ASPNETCORE_ENVIRONMENT=Development" Command="dotnet tool restore" />
<Exec EnvironmentVariables="ASPNETCORE_ENVIRONMENT=Development" Command="dotnet tool run swagger tofile --output $(ProjectDir)openapi.json $(ApiAssembly) v1" />
</Target>The preceding code defines the
BuildSwaggerFile
target, which is executed after theBuild
target. There are twoExec
tasks in theBuildSwaggerFile
target:- The first
Exec
task restores the Swashbuckle CLI tool. It is useful when you use the pipeline to build the project. - The second
Exec
task runs theswagger tofile
command to generate the OpenAPI specification file. TheApiAssembly
property is used to specify the Web API project.
- The first
Right-click the project and select Rebuild
. If the build is successful, you should see the openapi.json
file in the project folder.
Creating a WebAPI client project
Next, let’s create a WebAPI client project. We can publish the client library to NuGet or distribute it as a package later.
Create a new project in the same solution. Choose Class Library template and click Next. Name the project
WebApiClientDemo.Client
.Delete the
Class1.cs
file in theWebApiClientDemo.Client
project.Add the reference to the
WebApiClientDemo
project, so, when we build the client project, the WebAPI project will be built first to ensure the OpenAPI specification file is up to date.Add the following code to the
WebApiClientDemo.Client.csproj
file:1
2
3
4
5
6<PropertyGroup>
<PackageId>WebApiClientDemo.Client</PackageId>
<RepositoryUrl>https://github.com/yanxiaodi/MyCodeSamples</RepositoryUrl>
<VersionPrefix>0.0.1</VersionPrefix>
<PackageTags>WebApiClient,Client</PackageTags>
</PropertyGroup>The preceding code defines the
PackageId
,RepositoryUrl
,VersionPrefix
, andPackageTags
properties. These properties are used to generate the NuGet package. You can find more information about the properties in the NuGet documentation. Update the properties with your own information.
Generating a WebAPI client with NSwag
Now that we have the OpenAPI specification file and a client project. Next, let’s use NSwag to generate the WebAPI client.
Create an interface named
IWebApiClient
in theWebApiClientDemo.Client
project. Add the following code to theIWebApiClient.cs
file:1
2
3
4namespace WebApiClientDemo.Client;
public interface IWebApiClient
{
}This interface will be used to define the REST API client library. All client classes will implement this interface.
Install the
NSwag.MSBuild
package in theWebApiClientDemo.Client
project. TheNSwag.MSBuild
package provides the MSBuild tasks to generate the REST API client library. Run the following command to install the package:1
dotnet add package NSwag.MSBuild
Create a
nswag.json
file in theMyWebApiDemo.WebApiClient
project and add the following code:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66{
"runtime": "Net80",
"documentGenerator": {
"fromDocument": {
"url": "../WebApiClientDemo/openapi.json",
"output": null,
"newLineBehavior": "Auto"
}
},
"codeGenerators": {
"openApiToCSharpClient": {
"generateClientClasses": true,
"generateClientInterfaces": true,
"clientBaseInterface": "IWebApiClient",
"generateDtoTypes": true,
"injectHttpClient": false,
"disposeHttpClient": true,
"generateExceptionClasses": true,
"exceptionClass": "SwaggerException",
"wrapDtoExceptions": true,
"useHttpClientCreationMethod": false,
"httpClientType": "System.Net.Http.HttpClient",
"useHttpRequestMessageCreationMethod": false,
"useBaseUrl": true,
"generateBaseUrlProperty": true,
"generateSyncMethods": false,
"exposeJsonSerializerSettings": false,
"clientClassAccessModifier": "public",
"typeAccessModifier": "public",
"generateContractsOutput": false,
"parameterDateTimeFormat": "s",
"generateUpdateJsonSerializerSettingsMethod": true,
"serializeTypeInformation": false,
"queryNullValue": "",
"className": "{controller}Client",
"operationGenerationMode": "MultipleClientsFromOperationId",
"generateOptionalParameters": false,
"generateJsonMethods": true,
"parameterArrayType": "System.Collections.Generic.IEnumerable",
"parameterDictionaryType": "System.Collections.Generic.IDictionary",
"responseArrayType": "System.Collections.ObjectModel.ObservableCollection",
"responseDictionaryType": "System.Collections.Generic.Dictionary",
"wrapResponses": false,
"generateResponseClasses": true,
"responseClass": "SwaggerResponse",
"namespace": "WebApiClientDemo.Client",
"requiredPropertiesMustBeDefined": true,
"dateType": "System.DateTime",
"dateTimeType": "System.DateTime",
"timeType": "System.TimeSpan",
"timeSpanType": "System.TimeSpan",
"arrayType": "System.Collections.ObjectModel.ObservableCollection",
"dictionaryType": "System.Collections.Generic.Dictionary",
"arrayBaseType": "System.Collections.ObjectModel.ObservableCollection",
"dictionaryBaseType": "System.Collections.Generic.Dictionary",
"classStyle": "Inpc",
"generateDefaultValues": true,
"generateDataAnnotations": true,
"excludedTypeNames": [],
"handleReferences": false,
"generateImmutableArrayProperties": false,
"generateImmutableDictionaryProperties": false,
"output": "WebApiClient.cs"
}
}
}The preceding code defines the
nswag.json
file, which is used to generate the REST API client library. There are some important properties in thenswag.json
file:runtime
: The target runtime for the client library.documentGenerator
: The document generator configuration. ThefromDocument
property specifies theurl
property of the OpenAPI specification. It can use a relative path to the OpenAPI specification file.generateClientClasses
: Whether to generate the client classes.generateClientInterfaces
: Whether to generate the client interfaces.clientBaseInterface
: The base interface for the client classes. In this case, we use theIWebApiClient
interface.className
: The class name for the client classes. The{controller}
placeholder is used to specify the controller name.namespace
: The namespace for the client classes.output
: The output file for the client library.
Update the
nswag.json
file with your own information. To learn more about thenswag.json
file, you can refer to the NSwag configuration documentation.Add the following code to the
WebApiClientDemo.Client.csproj
file:1
2
3<Target Name="NSwag" AfterTargets="Build">
<Exec WorkingDirectory="$(ProjectDir)" EnvironmentVariables="ASPNETCORE_ENVIRONMENT=Development" Command="$(NSwagExe_Net80) run /input:nswag.json" />
</Target>The preceding code executes the
nswag.json
file to generate the REST API client library. The command isnswag run /input:nswag.json
. To learn more about the NSwag commands, you can refer to the NSwag command-line documentation.Right-click the
WebApiClientDemo.Client
project and selectRebuild
. If the build is successful, you should see theWebApiClient.cs
file in theWebApiClientDemo.Client
project. This file contains the REST API client library.
Use System.Text.Json instead of Newtonsoft.Json
By default, NSwag uses Newtonsoft.Json
to serialize and deserialize JSON data. If you want to use System.Text.Json
instead of Newtonsoft.Json
, you can add the following code to the nswag.json
file:
1 | { |
The jsonLibrary
property is used to specify the JSON library. The jsonSerializerSettingsTransformationMethod
property is used to specify the method to transform the JSON serializer settings. If you don’t need to transform the JSON serializer settings, you can set it to null
.
Customizing the method names
Check the generated WebApiClient.cs
file, and you can find the following code:
1 | System.Threading.Tasks.Task<System.Collections.ObjectModel.ObservableCollection<WeatherForecast>> GetWeatherForecastAsync(); |
How does NSwag know the method name is GetWeatherForecastAsync
?
In the nswag.json
file, you can find the following configuration:
1 | { |
The operationGenerationMode
property is used to specify the operation generation mode. The MultipleClientsFromOperationId
mode generates the client classes based on the operation ID. This means that the method names in the client classes are based on the operation ID. Check the OpenAPI specification file, and you can find the following code:
1 | "/WeatherForecast": { |
The operationId
property is used to specify the operation ID. In this case, the operation ID is WeatherForecast
. Where is it from?
Open the WeatherForecastController.cs
file, and you can find the following code:
1 | [ ] |
The Name
property of the HttpGet
attribute is used to specify the operation ID. So, the method name in the client class is GetWeatherForecastAsync
.
In the sample project, you can find a controller named ProductsController
. The actions in this controller don’t have the Name
property. So what will happen?
If we don’t specify the Name
property for the action methods, the OpenAPI specification file will not contain the operationId
property. NSwag will use the HTTP methods instead. So you will see the following methods:
1 | System.Threading.Tasks.Task<string> ProductsGETAsync(int id); |
This looks a little bit weird. To solve this issue, you can add the Name
property to the action methods to specify the operation ID. However, if you have many action methods, it can be tedious. You can configure the operation IDs for action methods in the Program.cs
file. Add the following code to the AddSwaggerGen()
method:
1 | options.CustomOperationIds(e => $"{e.ActionDescriptor.RouteValues["controller"]}_{e.ActionDescriptor.RouteValues["action"]}"); |
The preceding will use the controller name and action name as the operation ID. Generate the OpenAPI specification file again, and you will see the endpoints now have the operation IDs, such as Products_GetProduct
, Products_CreateProduct
, Products_UpdateProduct
, and Products_DeleteProduct
, etc. The generated client classes will have the method names based on the operation IDs:
1 | System.Threading.Tasks.Task<string> GetProductAsync(int id); |
Now it looks better.
Providing an extension method to register the REST API client library
Next, we can provide an extension method to register the REST API client library in other projects.
Create a
WebApiClientOptions
class to store the base URL of the REST API in theWebApiClientDemo.Client
project. Add the following code to theWebApiClientOptions.cs
file:1
2
3
4
5namespace WebApiClientDemo.Client;
public class WebApiClientOptions
{
public string? BaseUrl { get; set; }
}You can add more configurations if you need.
Create a
WebApiClientExtensions.cs
class and add the following code:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30using Microsoft.Extensions.DependencyInjection;
namespace WebApiClientDemo.Client;
public static class WebApiClientExtensions
{
private const string BaseUrlNullOrEmptyErrorMessage = "Base Url can't be null or empty.";
public static IServiceCollection AddWebApiClients(this IServiceCollection services, WebApiClientOptions options)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
if (options == null || string.IsNullOrWhiteSpace(options.BaseUrl))
{
throw new ArgumentNullException(nameof(options), BaseUrlNullOrEmptyErrorMessage);
}
services.AddHttpClient<IWeatherForecastClient, WeatherForecastClient>(client =>
{
client.BaseAddress = new Uri(options.BaseUrl);
});
services.AddHttpClient<IProductsClient, ProductsClient>(client =>
{
client.BaseAddress = new Uri(options.BaseUrl);
});
return services;
}
}The preceding code provides an extension method to register the REST API client library. If your web API has more controllers, you need to register them in the
AddWebApiClients()
method.
Next, you can publish this project as a NuGet package and share it with other teams. As the API client code is generated in the build process, you can easily update the client library when the WebAPI is updated in your CI/CD pipeline. The other teams can install the package and register the API client library using the extension method in their projects. This can save a lot of time and reduce the chance of errors.
If you want to publish this project as a NuGet package, you can refer to the Publishing a package documentation, or check my article Using Azure Pipelines to publish the NuGet package from GitHub repo.
Summary
In this article, I showed you how to generate a WebAPI client for your ASP.NET Core WebAPI with Swashbuckle and NSwag. We used Swashbuckle to generate the OpenAPI specification file and NSwag to generate the REST API client library. We also provided an extension method to register the REST API client library in other projects. NSwag provides a GUI tool to generate the client library, but it is not suitable for the CI/CD pipeline. By using the Swashbuckle CLI tool and the NSwag CLI tool, we can generate the client library in the build process of the CI/CD pipelines. This can speed up the development process and reduce the chance of errors.