Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HttpClientFactory: Typed HttpClient with additional constructor arguments

With HttpClientFactory we can configure dependancy injection to create and manage the lifetime of HttpClients:

public class GitHubService : IGitHubService
{
    public HttpClient Client { get; }

    public GitHubService(HttpClient client)
    {
        client.BaseAddress = new Uri("https://api.github.com/");
        // GitHub API versioning
        client.DefaultRequestHeaders.Add("Accept",
            "application/vnd.github.v3+json");
        // GitHub requires a user-agent
        client.DefaultRequestHeaders.Add("User-Agent",
            "HttpClientFactory-Sample");

        Client = client;
    }

    public async Task<IEnumerable<GitHubIssue>> GetAspNetDocsIssues()
    {
        var response = await Client.GetAsync(
            "/repos/aspnet/AspNetCore.Docs/issues?state=open&sort=created&direction=desc");

        response.EnsureSuccessStatusCode();

        using var responseStream = await response.Content.ReadAsStreamAsync();
        return await JsonSerializer.DeserializeAsync
            <IEnumerable<GitHubIssue>>(responseStream);
    }
}

Then in Startup.cs we configure DI:

services.AddHttpClient<GitHubService>();

However, if the typed client has additional constructor arguments, how should these be provided? For example, if a repository name was to be passed in:

public class GitHubService : IGitHubService
{
    public HttpClient Client { get; }
    private readonly string _repositoryName;

    public GitHubService(HttpClient client, string repositoryName)
    {
        _repositoryName = repositoryName;

        client.BaseAddress = new Uri("https://api.github.com/");
        // GitHub API versioning
        client.DefaultRequestHeaders.Add("Accept",
            "application/vnd.github.v3+json");
        // GitHub requires a user-agent
        client.DefaultRequestHeaders.Add("User-Agent",
            "HttpClientFactory-Sample");

        Client = client;
    }

    public async Task<IEnumerable<GitHubIssue>> GetAspNetDocsIssues()
    {
        var response = await Client.GetAsync(
            $"/repos/aspnet/{_repositoryName}/issues?state=open&sort=created&direction=desc");

        response.EnsureSuccessStatusCode();

        using var responseStream = await response.Content.ReadAsStreamAsync();
        return await JsonSerializer.DeserializeAsync
            <IEnumerable<GitHubIssue>>(responseStream);
    }
}

Perhaps this isn't a realistic example, but how would dependency injection be configured to provide the repository name?

like image 808
philreed Avatar asked Dec 05 '19 11:12

philreed


2 Answers

An alternative if you don't want to use the named clients is to create a custom class for the extra parameters. Because the problem is that it does not know what to resolve for the type string, what you can do is to create an object with a string property that will contain the value that you want to pass, register it as a singleton and let the container resolve it.

Create a class that contains all the extra parameters. In your case "repositoryName"

public class RepositoryConfig
{
    public string RepositoryName {get; set;}
}

Register the new class

services.AddSingleton(new RepositoryConfig { RepositoryName = "MyRepo"});

Then register the HttpClient

services.AddHttpClient<IGitHubService, GitHubService>();

Now your class will be instantiated correctly.

like image 153
Carlosoj Avatar answered Sep 28 '22 08:09

Carlosoj


I managed to get this working by switching to named clients:

//To start with, create a named client:
services.AddHttpClient("GitHubClient", ctx => { ctx.BaseAddress = new Uri("https://api.github.com/"); });

//Then set up DI for the TypedClient
services.AddTransient<IGitHubService>(ctx =>
{
    var clientFactory = ctx.GetRequiredService<IHttpClientFactory>();
    var httpClient = clientFactory.CreateClient("GitHubClient");
 
    return new GitHubService(httpClient, repositoryName);
});
  
like image 31
philreed Avatar answered Sep 28 '22 07:09

philreed