Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dotnet tutorial kiota dependency injection #89

Merged
merged 13 commits into from
Jul 2, 2024
2 changes: 2 additions & 0 deletions OpenAPI/kiota/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,7 @@
items:
- name: Add an API to search
href: add-api.md
- name: Use Kiota with Dependency Injection in .NET
href: tutorials/dotnet-dependencyinjection.md

Check warning on line 81 in OpenAPI/kiota/toc.yml

View workflow job for this annotation

GitHub Actions / Lint and spell-check

Unknown word (dependencyinjection)
svrooij marked this conversation as resolved.
Show resolved Hide resolved
- name: Support
href: support.md
3 changes: 2 additions & 1 deletion OpenAPI/kiota/tutorials/dotnet-azure.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ In this tutorial, you will generate an API client that uses [Microsoft identity
## Required tools

- [.NET SDK 8.0](https://get.dot.net/8)
- [Kiota CLI](/openapi/kiota/install?tabs=bash&wt.mc_id=SEC-MVP-5004985#install-as-net-tool)

## Create a project

Expand All @@ -26,7 +27,7 @@ dotnet new gitignore

## Add dependencies

Before you can compile and run the generated API client, you will need to make sure the generated source files are part of a project with the required dependencies. Your project must have a reference to the [abstraction package](https://github.com/microsoft/kiota-abstractions-dotnet). Additionally, you must either use the Kiota default implementations or provide your own custom implementations of of the following packages.
Before you can compile and run the generated API client, you will need to make sure the generated source files are part of a project with the required dependencies. Your project must have a reference to the [abstraction package](https://github.com/microsoft/kiota-abstractions-dotnet). Additionally, you must either use the Kiota default implementations or provide your own custom implementations of the following packages.

- Authentication ([Kiota default Azure authentication](https://github.com/microsoft/kiota-authentication-azure-dotnet))
- HTTP ([Kiota default HttpClient-based implementation](https://github.com/microsoft/kiota-http-dotnet))
Expand Down
288 changes: 288 additions & 0 deletions OpenAPI/kiota/tutorials/dotnet-dependencyinjection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
---
title: Use Kiota with dependency injection in .NET
description: Learn how you can combine Kiota with dependency injection in .NET to build API clients.
author: svrooij

Check warning on line 4 in OpenAPI/kiota/tutorials/dotnet-dependencyinjection.md

View workflow job for this annotation

GitHub Actions / Lint and spell-check

Unknown word (svrooij)
svrooij marked this conversation as resolved.
Show resolved Hide resolved
ms.author: vibiret
ms.topic: tutorial
date: 05/10/2024
---

# Register a Kiota API client in .NET with dependency injection

In this tutorial, you will learn how to register a [Kiota API client](/openapi/kiota/tutorials/dotnet-azure?wt.mc_id=SEC-MVP-5004985) with [dependency injection](/dotnet/core/extensions/dependency-injection-usage?wt.mc_id=SEC-MVP-5004985).

## Required tools

- [.NET SDK 8.0](https://get.dot.net/8)
- [Kiota CLI](/openapi/kiota/install?tabs=bash&wt.mc_id=SEC-MVP-5004985#install-as-net-tool)

## Create a project

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

```dotnetcli
dotnet new webapi -o kiota-with-di

Check warning on line 24 in OpenAPI/kiota/tutorials/dotnet-dependencyinjection.md

View workflow job for this annotation

GitHub Actions / Lint and spell-check

Unknown word (webapi)
svrooij marked this conversation as resolved.
Show resolved Hide resolved
cd kiota-with-di
dotnet new gitignore
```

## Add dependencies

Before you can compile and run the generated API client, you will need to make sure the generated source files are part of a project with the required dependencies. Your project must have a reference to the [abstraction package](https://github.com/microsoft/kiota-abstractions-dotnet) and some default implementations.
svrooij marked this conversation as resolved.
Show resolved Hide resolved

- HTTP ([Kiota default HttpClient-based implementation](https://github.com/microsoft/kiota-http-dotnet))
- JSON serialization ([Kiota default](https://github.com/microsoft/kiota-serialization-json-dotnet))
- HttpClient support for Dependency Injection [Microsoft.Extensions.Http](https://www.nuget.org/packages/Microsoft.Extensions.Http)

If you expect to use other serialization formats, you can add the following packages.

- Form serialization ([Kiota default](https://github.com/microsoft/kiota-serialization-form-dotnet))
- Text serialization ([Kiota default](https://github.com/microsoft/kiota-serialization-text-dotnet))
- Multipart serialization ([Kiota default](https://github.com/microsoft/kiota-serialization-multipart-dotnet))

Run the following commands to get the required dependencies.

```dotnetcli
dotnet add package Microsoft.Extensions.Http
dotnet add package Microsoft.Kiota.Abstractions
dotnet add package Microsoft.Kiota.Http.HttpClientLibrary
dotnet add package Microsoft.Kiota.Serialization.Json

# Uncomment the following lines if you expect to use other serialization formats
# dotnet add package Microsoft.Kiota.Serialization.Form
# dotnet add package Microsoft.Kiota.Serialization.Text
# dotnet add package Microsoft.Kiota.Serialization.Multipart
```

## Generate the API client

Kiota generates API clients from OpenAPI documents. Create a file named _github-releases.yml_ and add the following.

```yaml
openapi: 3.0.3
info:
title: GitHub Release API
version: 1.0.0
servers:
- url: https://api.github.com
description: GitHub API
variables:
version:
default: '2022-11-28'
description: GitHub API version
paths:
/repos/{owner}/{repo}/releases/latest:
get:
summary: Get the latest release
parameters:
- name: owner
in: path
required: true
description: The owner of the repository
schema:
type: string
- name: repo
in: path
required: true
description: The repository name
schema:
type: string
responses:
200:
description: Success!
content:
application/json:
schema:
$ref: "#/components/schemas/github.release"
404:
description: Not found
content:
application/json:
schema:
$ref: "#/components/schemas/github.error"

components:
schemas:
github.release:
type: object
properties:
id:
type: integer
description: The release ID
tag_name:
type: string
description: The release tag name
name:
type: string
description: The release name
body:
type: string
description: The release body
created_at:
type: string
format: date-time
description: The release creation date
published_at:
type: string
format: date-time
description: The release publication date
github.error:
type: object
properties:
message:
type: string
description: The error message
documentation_url:
type: string
description: The error documentation URL
```

You can then use the [Kiota](/openapi/kiota/install?tabs=bash&wt.mc_id=SEC-MVP-5004985#install-as-net-tool) command line tool to generate the API client classes. We are using the [deserializer](/openapi/kiota/using#--deserializer---ds) and [serializer](/openapi/kiota/using#--serializer---s) options to specify the serialization format, to only use JSON serialization.

```bash
kiota generate -l csharp -d github-releases.yml -c GitHubClient -n GitHub.ApiClient -o ./GitHub --deserializer Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory --serializer Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory
svrooij marked this conversation as resolved.
Show resolved Hide resolved
```

## Create extension methods

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

```csharp
using Microsoft.Kiota.Http.HttpClientLibrary.Middleware;
using Microsoft.Kiota.Http.HttpClientLibrary.Middleware.Options;

/// <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>

Check warning on line 162 in OpenAPI/kiota/tutorials/dotnet-dependencyinjection.md

View workflow job for this annotation

GitHub Actions / Lint and spell-check

Unknown word (cref)
svrooij marked this conversation as resolved.
Show resolved Hide resolved
/// <returns><see cref="IServiceCollection"/> as per convention</returns>

Check warning on line 163 in OpenAPI/kiota/tutorials/dotnet-dependencyinjection.md

View workflow job for this annotation

GitHub Actions / Lint and spell-check

Unknown word (cref)
/// <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>

Check warning on line 164 in OpenAPI/kiota/tutorials/dotnet-dependencyinjection.md

View workflow job for this annotation

GitHub Actions / Lint and spell-check

Unknown word (cref)
public static IServiceCollection AddKiotaHandlers(this IServiceCollection services)
{
services.AddTransient<UriReplacementHandler<UriReplacementHandlerOption>>();
baywet marked this conversation as resolved.
Show resolved Hide resolved
services.AddTransient<RetryHandler>();
services.AddTransient<RedirectHandler>();
services.AddTransient<ParametersNameDecodingHandler>();
services.AddTransient<UserAgentHandler>();
services.AddTransient<HeadersInspectionHandler>();
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)"/>.

Check warning on line 182 in OpenAPI/kiota/tutorials/dotnet-dependencyinjection.md

View workflow job for this annotation

GitHub Actions / Lint and spell-check

Unknown word (cref)
/// The order in which the handlers are added is important, as it defines the order in which they will be executed.
/// <see href="https://github.com/microsoft/kiota-http-dotnet/blob/c1c295d3b0ebb2182b66d9a6858241117b59b540/src/KiotaClientFactory.cs#L50-L62">KiotaClientFactory.cs</see> for the default order.
/// </remarks>
public static IHttpClientBuilder AttachKiotaHandlers(this IHttpClientBuilder builder)
{
builder.AddHttpMessageHandler<UriReplacementHandler<UriReplacementHandlerOption>>();
svrooij marked this conversation as resolved.
Show resolved Hide resolved
builder.AddHttpMessageHandler<RetryHandler>();
builder.AddHttpMessageHandler<RedirectHandler>();
builder.AddHttpMessageHandler<ParametersNameDecodingHandler>();
builder.AddHttpMessageHandler<UserAgentHandler>();
builder.AddHttpMessageHandler<HeadersInspectionHandler>();
return builder;
}
}
```

## Create a client factory

Create a new file named _GitHubClientFactory.cs_ in the _GitHub_ folder and add the following code.

This is what you call 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 will be registering the factory in the dependency injection container as well, because it will get the `HttpClient` injected in the constructor.

```csharp
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.

```csharp
using GitHub;
...

// Add Kiota handlers to the DI container
svrooij marked this conversation as resolved.
Show resolved Hide resolved
builder.Services.AddKiotaHandlers();

// Register the factory for the GitHub client
builder.Services.AddHttpClient<GitHubClientFactory>((sp, client) => {
client.BaseAddress = new Uri("https://api.github.com");
client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
}).AttachKiotaHandlers();

// Register the GitHub client
builder.Services.AddTransient(sp=> sp.GetRequiredService<GitHubClientFactory>().GetClient());
svrooij marked this conversation as resolved.
Show resolved Hide resolved
```

## 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.

```csharp
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();
```

## Run the application

Run the following command in your project directory to start the application.

```dotnetcli
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](/openapi/kiota/authentication?wt.mc_id=SEC-MVP-5004985).

What to remember is that 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.
svrooij marked this conversation as resolved.
Show resolved Hide resolved

In [this example](https://github.com/svrooij/KiotaWithDependencyInjection/blob/d1ee972e8ba06c4f9707b78dfa981f5f57fbf7e6/KiotaWithDependencyInjection/Spotify/SpotifyFactory.cs) there is an `IAccessTokenProvider` injected that is then [used](https://github.com/svrooij/KiotaWithDependencyInjection/blob/d1ee972e8ba06c4f9707b78dfa981f5f57fbf7e6/KiotaWithDependencyInjection/Spotify/SpotifyFactory.cs#L36) for the [BaseBearerTokenAuthenticationProvider](/openapi/kiota/authentication?tabs=csharp&wt.mc_id=SEC-MVP-5004985#base-bearer-token-authentication-provider) that is available in Kiota.
svrooij marked this conversation as resolved.
Show resolved Hide resolved

## See also

- [KiotaWithDependencyInjection repository](https://github.com/svrooij/KiotaWithDependencyInjection) contains the code that inspired this tutorial.
- [Build API clients for .NET with Microsoft Identity authentication](/openapi/kiota/tutorials/dotnet-azure?wt.mc_id=SEC-MVP-5004985) shows how to use Kiota with Microsoft Identity authentication.
- [ToDoItem Sample API](https://github.com/microsoft/kiota-samples/tree/main/sample-api) implements a sample OpenAPI in ASP.NET Core and sample clients in multiple languages.
Loading