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.csfile 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
OpenApiInfoobject 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.csprojfile 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 tofilecommand is used to generate the OpenAPI specification file. - The
--outputoption is used to specify the output file. - The
bin\Debug\net8.0\WebApiClientDemo.dllis the relative path to the Web API project. - The
v1option is the name of the OpenAPI document, which is defined in theAddSwaggerGen()method.
If the command is successful, you should see the
swagger-api.jsonfile 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.jsonfile 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.jsonfile and add the Swashbuckle CLI tool to the project. You can find thedotnet-tools.jsonfile 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
.csprojfile to include theswagger tofilecommand in the build process. Add the following code to the.csprojfile: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
SolutionDirectoryandApiAssemblyproperties. TheApiAssemblyproperty 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
.csprojfile: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
BuildSwaggerFiletarget, which is executed after theBuildtarget. There are twoExectasks in theBuildSwaggerFiletarget:- The first
Exectask restores the Swashbuckle CLI tool. It is useful when you use the pipeline to build the project. - The second
Exectask runs theswagger tofilecommand to generate the OpenAPI specification file. TheApiAssemblyproperty 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.csfile in theWebApiClientDemo.Clientproject.Add the reference to the
WebApiClientDemoproject, 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.csprojfile: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, andPackageTagsproperties. 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
IWebApiClientin theWebApiClientDemo.Clientproject. Add the following code to theIWebApiClient.csfile: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.MSBuildpackage in theWebApiClientDemo.Clientproject. TheNSwag.MSBuildpackage 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.jsonfile in theMyWebApiDemo.WebApiClientproject 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.jsonfile, which is used to generate the REST API client library. There are some important properties in thenswag.jsonfile:runtime: The target runtime for the client library.documentGenerator: The document generator configuration. ThefromDocumentproperty specifies theurlproperty 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 theIWebApiClientinterface.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.jsonfile with your own information. To learn more about thenswag.jsonfile, you can refer to the NSwag configuration documentation.Add the following code to the
WebApiClientDemo.Client.csprojfile: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.jsonfile 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.Clientproject and selectRebuild. If the build is successful, you should see theWebApiClient.csfile in theWebApiClientDemo.Clientproject. 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
WebApiClientOptionsclass to store the base URL of the REST API in theWebApiClientDemo.Clientproject. Add the following code to theWebApiClientOptions.csfile: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.csclass 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.