Generating a Typed Client for use with HttpClientFactory using NSwag
Tags: dotnetcore, csharp, dotnet
Published: 2019-03-18 11:11:42

---
title: Generating a Typed Client for use with HttpClientFactory using NSwag
published: true
tags: dotnetcore,csharp,dotnet
canonical_url: https://stu.dev/generating-typed-client-for-httpclientfactory-with-nswag/
---

If you're a .NET developer building web apps or microservices, odds are at some point you're going to want to call an HTTP API from an ASP.NET Core app. This post covers how to create a client using NSwag, with the appropriate settings for using it with `HttpClientFactory`.

## Typed Clients and `HttpClientFactory`

For a proper introduction to these concepts I encourage you to read [Steve Gordon](https://twitter.com/stevejgordon)'s HttpClientFactory in ASP.NET Core 2.1 series, [An Introduction to HttpClientFactory](https://www.stevejgordon.co.uk/introduction-to-httpclientfactory-aspnetcore) and [Defining Named and Typed Clients](https://www.stevejgordon.co.uk/httpclientfactory-named-typed-clients-aspnetcore), but the really short version is:

- `HttpClientFactory` - This was introduced to solve the problems around managing the lifetime of `HttpClient`s and their handlers, which historically has been problematic.
- Typed Clients - This is a feature of `HttpClientFactory` which lets you register a class which simply wraps `HttpClient` (i.e. takes one in its constructor), thus giving you a typed client to your API, while not owning the lifetime of any `HttpClient`s, letting `HttpClientFactory` do its thing.

## NSwag

Where does [NSwag](http://nswag.org) fit into this?

NSwag is, on one hand, like [Swashbuckle](https://github.com/domaindrivendev/Swashbuckle.AspNetCore), in that with a couple of lines of code you can have your ASP.NET Core application serving up a swagger doc and swagger ui within your app at runtime. Then it also has the functionality of [AutoRest](https://github.com/Azure/autorest), where it can take a swagger file and generate a C# typed client for you (and other languages such as TypeScript).

There are a couple of things that impress me about NSwag:

- The number of integrations - it's a swiss army knife! Just take a minute to read the [README](https://github.com/RSuter/NSwag/blob/master/README.md), you quickly get the idea, it can generate swagger or OpenAPI file not just at runtime, but at build time from your web api assembly. Then you can generate clients in various languages with lots of settings to play around with.
- [Rico Suter](https://twitter.com/RicoSuter), who is the primary maintainer, is continuously contributing to it and is so responsive to issues, and checking in fixes and features. It's like he has a team running his GitHub account!

## Starting with swagger.json

We are going to be generating a client from `swagger.json`, if you need to produce this from your own ASP.NET Core application then you can generate a `swagger.json` using the NSwag CLI, [see here](https://github.com/RSuter/NSwag/wiki/AspNetCoreToSwaggerCommand).

## Generating the Client in Build

For this, we will use the `NSwag.ConsoleCore` CLI tool package (we could also use the [NSwag.MSBuild](https://www.nuget.org/packages/NSwag.MSBuild/) package, the process is largely the same). I'm using the [Pet Store swagger](https://petstore.swagger.io/#/), and start by dropping the `swagger.json` into my project folder.

There are 2 ways to pass config into the NSwag commands, one is via command line args, the other is via a JSON config file which is what I'll be using here.

Firs create an empty library (here we will target `netstandard2.0`) and add the following snippet to an `ItemGroup` in your `.csproj` file:

```xml
<DotNetCliToolReference Include="NSwag.ConsoleCore" Version="12.0.15" />
```

After a `dotnet restore` we can now run in the project folder `dotnet nswag new`, this will create us a default config file, we can strip it down so it [looks like this](https://github.com/slang25/nswag-petstore-client/blob/master/NSwag.PetStore.Client/nswag.json). Here's a trimmed down version:

```json
{
  "runtime": "NetCore22",
  "defaultVariables": null,
  "swaggerGenerator":{
    "fromSwagger":{
      "json":"$(InputSwagger)"
    }
  },
  "codeGenerators": {
    "swaggerToCSharpClient": {
      ... OMITTING LOTS OF DEFAULT CONFIG ...
      "generateClientInterfaces": true,
      "injectHttpClient": true,
      "disposeHttpClient": false,
      "generateExceptionClasses": true,
      "exceptionClass": "$(ClientName)Exception",
      "useBaseUrl": false,
      "className": "$(ClientName)Client",
      "generateOptionalParameters": true,
      "generateJsonMethods": false,
      "namespace": "$(ClientNamespace)",
      "classStyle": "Poco",
      "output": "$(GeneratedSwaggerClientFile)"
    }
  }
}
```

Here are settings that I changed from their defaults:

- `generateClientInterfaces` - This gives us an interface for our typed client, abstracting the implementation away. This may seem like something you wouldn't think of doing without, but there is merit in using the implementation of the typed client under test and mocking at the `HttpClient` level (shout out to [httpclient-interception](https://github.com/justeat/httpclient-interception)).
- `InjectHttpClient` - This creates a constructor to the client that takes in a `HttpClient`, which is what we need to use it as a typed client with `HttpClientFactory`.
- `disposeHttpClient` - It mostly doesn't matter if you call `Dispose` on `HttpClient` when used with `HttpClientFactory`, it's mostly a no-op. However, for correctness sake, it would be weird for the typed client to dispose of something it doesn't own.
- `generateOptionalParameters` - By default NSwag will create 2 methods per operation, one with and one without a `CancellationToken`, enabling this will combine them.
- `generateJsonMethods` - By default NSwag will add instance methods that serialize and deserialize types, they just delegate to one-liner static methods from Json.NET. I like my POCOs nice and plain, so I disable this.
- `useBaseUrl` - This is an important one, we want to use the `BaseAddress` of `HttpClient` only, we don't want to override that with a property on the typed client (which is what will happen by default), so we set this to `false`. We can configure the `BaseAddress` at the time we register the typed client.
- `exceptionClass` - The type of exception that is thrown on error, by default it would be `SwaggerException`, which feels like an implementation leak.
- `classStyle` - By default NSwag uses the style `Inpc` which is short for `INotifyPropertyChange`, which is a strange default, I can only think it would be useful if you were building an MVVM client app and wanted to reuse the classes to bind to directly (still feels wrong to me). By selecting `Poco`, we end up with just a typical class with getter and setter properties.

You can see I use variables in this file, such as `"$(InputSwagger)"`, we can pass these in via the `nswag` command.

What we want to do is to just do a `dotnet build` and to have the project automatically create the generated `.cs` file, and include it in the files to build. So here's the `.csproj`:

```xml
<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <TargetFramework>netstandard2.0</TargetFramework>
        <DebugType>embedded</DebugType>
        <EmbedAllSources>true</EmbedAllSources>
    </PropertyGroup>
    <ItemGroup>
        <PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
        <PackageReference Include="System.ComponentModel.Annotations" Version="4.5.0" />
        <DotNetCliToolReference Include="NSwag.ConsoleCore" Version="12.0.15" />
    </ItemGroup>

    <Target Name="GenerateNSwagClient">
        <PropertyGroup>
            <InputSwagger>swagger.json</InputSwagger>
            <ClientName>PetStore</ClientName>
            <GeneratedSwaggerClientFile Condition="'$(GeneratedSwaggerClientFile)' ==''">$(IntermediateOutputPath)$(MSBuildProjectName).$(ClientName)Client.cs</GeneratedSwaggerClientFile>
        </PropertyGroup>
        <Exec Command="dotnet nswag run nswag.json /variables:InputSwagger=$(InputSwagger),ClientName=$(ClientName),ClientNamespace=$(RootNamespace),GeneratedSwaggerClientFile=$(GeneratedSwaggerClientFile)" />
    </Target>

    <Target Name="IncludeNSwagClient" BeforeTargets="CoreCompile" DependsOnTargets="GenerateNSwagClient">
        <ItemGroup Condition="Exists('$(GeneratedSwaggerClientFile)')">
            <Compile Include="$(GeneratedSwaggerClientFile)" />
            <FileWrites Include="$(GeneratedSwaggerClientFile)" />
        </ItemGroup>
    </Target>
</Project>
```

Things to note:

- The only thing in this file that is specific to the Pet Store swagger, is the `<ClientName>PetStore</ClientName>`, feel free to use this as a recipe for your own clients.
- We hook our targets before `CoreCompile`, giving us the opportunity to add files to compile.
- The generated file goes into the `IntermediateOutputPath`, this is the configuration-specific folder inside of `obj`, this is exactly how `AssemblyInfo.cs` is generated, and is likely already part of your `.gitignore`.
- We have included the required `Newtonsoft.Json` and `System.ComponentModel.Annotations` packages.
- Variables are passed into the `nswag.json` using the `variables` argument.
- `<DebugType>embedded</DebugType>` and `<EmbedAllSources>true</EmbedAllSources>` gives us one `.dll` which contains an embedded PDB file, which contains all of the source within it for debugging purposes. You could also use [symbol packages](https://docs.microsoft.com/en-us/nuget/create-packages/symbol-packages-snupkg), which in my option is a step backwards from this solution.

## Using the Client with `HttpClientFactory`

Right, we have our package/project, let's use it!

Let's create a new ASP.NET Core Web API using the template, and add a reference to our client project and add the `Microsoft.Extensions.Http` package. [See here](https://github.com/slang25/nswag-petstore-client/blob/master/SampleApp/SampleApp.csproj) for an example.

Now we can register our client in the `ConfigureServices` method, like this:

```csharp
services.AddHttpClient<IPetStoreClient, PetStoreClient>(c => c.BaseAddress = new Uri("https://petstore.swagger.io/v2/"));
```

And that's it! Now we can start using our client, here's an example from our controller:

```csharp
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    readonly IPetStoreClient petStoreClient;

    public ValuesController(IPetStoreClient petStoreClient)
    {
        this.petStoreClient = petStoreClient;
    }

    [HttpGet]
    public async Task<ActionResult<IEnumerable<string>>> Get(CancellationToken ct)
    {
        var stu = await petStoreClient.GetUserByNameAsync("Stu", ct);
        return new[] { stu.Email };
    }
}
```

You can see all of the [code here](https://github.com/slang25/nswag-petstore-client).