Register a Kiota API client in .NET with dependency injection

In this tutorial, you learn how to register a Kiota API client with dependency injection.

Required tools

Create a project

Run the following command in the directory you want to create a new project.

dotnet new webapi -o kiota-with-di
cd kiota-with-di
dotnet new gitignore

Add dependencies

Before you can compile and run the generated API client, ensure the generated source files are part of a project with the required dependencies. Your project must reference the bundle package. For more information about Kiota dependencies, see the dependencies documentation.

Run the following commands to get the required dependencies.

dotnet add package Microsoft.Extensions.Http
dotnet add package Microsoft.Kiota.Bundle

Generate the API client

Kiota generates API clients from OpenAPI documents. Let's download the GitHub API specs and generate the API client.

You can then use the Kiota command line tool to generate the API client classes.

# Download the specs to ./github-api.json
kiota download apisguru::github.com -o ./github-api.json
# Generate the client, path filter for just releases
kiota generate -l csharp -d github-api.json -c GitHubClient -n GitHub.ApiClient -o ./GitHub --include-path "/repos/{owner}/{repo}/releases/*" --clean-output

Create extension methods

Create a new file named KiotaServiceCollectionExtensions.cs in the root of your project and add the following code.

using Microsoft.Kiota.Http.HttpClientLibrary;

/// <summary>
/// Service collection extensions for Kiota handlers.
/// </summary>
public static class KiotaServiceCollectionExtensions
{
    /// <summary>
    /// Adds the Kiota handlers to the service collection.
    /// </summary>
    /// <param name="services"><see cref="IServiceCollection"/> to add the services to</param>
    /// <returns><see cref="IServiceCollection"/> as per convention</returns>
    /// <remarks>The handlers are added to the http client by the <see cref="AttachKiotaHandlers(IHttpClientBuilder)"/> call, which requires them to be pre-registered in DI</remarks>
    public static IServiceCollection AddKiotaHandlers(this IServiceCollection services)
    {
        // Dynamically load the Kiota handlers from the Client Factory
        var kiotaHandlers = KiotaClientFactory.GetDefaultHandlerTypes();
        // And register them in the DI container
        foreach(var handler in kiotaHandlers)
        {
            services.AddTransient(handler);
        }

        return services;
    }

    /// <summary>
    /// Adds the Kiota handlers to the http client builder.
    /// </summary>
    /// <param name="builder"></param>
    /// <returns></returns>
    /// <remarks>
    /// Requires the handlers to be registered in DI by <see cref="AddKiotaHandlers(IServiceCollection)"/>.
    /// The order in which the handlers are added is important, as it defines the order in which they will be executed.
    /// </remarks>
    public static IHttpClientBuilder AttachKiotaHandlers(this IHttpClientBuilder builder)
    {
        // Dynamically load the Kiota handlers from the Client Factory
        var kiotaHandlers = KiotaClientFactory.GetDefaultHandlerTypes();
        // And attach them to the http client builder
        foreach(var handler in kiotaHandlers)
        {
            builder.AddHttpMessageHandler((sp) => (DelegatingHandler)sp.GetRequiredService(handler));
        }    

        return builder;
    }
}

Create a client factory

Create a new file named GitHubClientFactory.cs in the GitHub folder and add the following code.

The code in these files implement a factory pattern, where you create a factory class that is responsible for creating an instance of the client. This way you have a single place that is responsible for creating the client, and you can easily change the way the client is created. In this sample we are registering the factory in the dependency injection container as well, because it gets the HttpClient injected in the constructor.

using GitHub.ApiClient;
using Microsoft.Kiota.Abstractions.Authentication;
using Microsoft.Kiota.Http.HttpClientLibrary;

namespace GitHub;

public class GitHubClientFactory
{
    private readonly IAuthenticationProvider _authenticationProvider;
    private readonly HttpClient _httpClient;

    public GitHubClientFactory(HttpClient httpClient)
    {
        _authenticationProvider = new AnonymousAuthenticationProvider();
        _httpClient = httpClient;
    }

    public GitHubClient GetClient() {
      return new GitHubClient(new HttpClientRequestAdapter(_authenticationProvider, httpClient: _httpClient));
    }
}

Register the API client

Open the Program.cs file and add the following code above the var app = builder.Build(); line. Sample Program.cs.

using GitHub;
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

// ----------- Add this part to register the generated client -----------
// Add Kiota handlers to the dependency injection container
builder.Services.AddKiotaHandlers();

// Register the factory for the GitHub client
builder.Services.AddHttpClient<GitHubClientFactory>((sp, client) => {
    // Set the base address and accept header
    // or other settings on the http client
    client.BaseAddress = new Uri("https://api.github.com");
    client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
}).AttachKiotaHandlers(); // Attach the Kiota handlers to the http client, this is to enable all the Kiota features.

// Register the GitHub client
builder.Services.AddTransient(sp => sp.GetRequiredService<GitHubClientFactory>().GetClient());
// ----------- Add this part to register the generated client end -------

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

var summaries = new[]
{
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

app.MapGet("/weatherforecast", () =>
{
    var forecast =  Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi();

// ----------- Add this part to create a new endpoint that uses the generated client -----------

app.MapGet("/dotnet/releases", async (GitHub.ApiClient.GitHubClient client, CancellationToken cancellationToken) =>
{
    var releases = await client.Repos["dotnet"]["runtime"].Releases["latest"].GetAsync(cancellationToken: cancellationToken);
    return releases;
})
.WithName("GetDotnetReleases")
.WithOpenApi();

// ----------- Add this part to create a new endpoint that uses the generated client end -----------

app.Run();

record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

Create an endpoint

We will now create an endpoint that uses the GitHub client from dependency injection to get the latest release of dotnet/runtime.

Open the Program.cs file and add the following code above the app.Run(); line. Sample Program.cs.

using GitHub;
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

// ----------- Add this part to register the generated client -----------
// Add Kiota handlers to the dependency injection container
builder.Services.AddKiotaHandlers();

// Register the factory for the GitHub client
builder.Services.AddHttpClient<GitHubClientFactory>((sp, client) => {
    // Set the base address and accept header
    // or other settings on the http client
    client.BaseAddress = new Uri("https://api.github.com");
    client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
}).AttachKiotaHandlers(); // Attach the Kiota handlers to the http client, this is to enable all the Kiota features.

// Register the GitHub client
builder.Services.AddTransient(sp => sp.GetRequiredService<GitHubClientFactory>().GetClient());
// ----------- Add this part to register the generated client end -------

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

var summaries = new[]
{
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

app.MapGet("/weatherforecast", () =>
{
    var forecast =  Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi();

// ----------- Add this part to create a new endpoint that uses the generated client -----------

app.MapGet("/dotnet/releases", async (GitHub.ApiClient.GitHubClient client, CancellationToken cancellationToken) =>
{
    var releases = await client.Repos["dotnet"]["runtime"].Releases["latest"].GetAsync(cancellationToken: cancellationToken);
    return releases;
})
.WithName("GetDotnetReleases")
.WithOpenApi();

// ----------- Add this part to create a new endpoint that uses the generated client end -----------

app.Run();

record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

Run the application

To start the application run the following command in your project directory.

dotnet run

You can now open a browser and navigate to http://localhost:{port}/dotnet/releases to see the latest release of dotnet/runtime. Alternatively, you can also go to http://localhost:{port}/swagger to see the Swagger UI with this new endpoint.

Authentication

The GitHubClientFactory class is responsible for creating the client, and it is also responsible for creating the IAuthenticationProvider. In this sample, we are using the AnonymousAuthenticationProvider, which is a simple provider that does not add any authentication headers. More details on authentication with Kiota can be found in the Kiota authentication documentation.

The GitHubClientFactory is used through dependency injection, which means you can have other types injected by the constructor that are needed for the authentication provider.

You might want to create a BaseBearerTokenAuthenticationProvider that is available in Kiota and expects an IAccessTokenProvider, which is just an interface with one method to provide an access token. Access tokens usually have a lifetime of 1 hour, which is why you need to get them at runtime and not set them at registration time.

See also