【问题标题】:HttpClientFactory: Typed HttpClient with additional constructor argumentsHttpClientFactory:带有附加构造函数参数的类型化 HttpClient
【发布时间】:2019-12-05 11:45:53
【问题描述】:

使用HttpClientFactory,我们可以配置依赖注入来创建和管理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);
    }
}

然后我们在 Startup.cs 中配置 DI:

services.AddHttpClient<GitHubService>();

但是,如果类型化客户端有额外的构造函数参数,应该如何提供这些? 例如,如果要传入存储库名称:

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);
    }
}

也许这不是一个现实的例子,但是如何配置依赖注入来提供存储库名称?

【问题讨论】:

    标签: c# asp.net asp.net-core asp.net-web-api


    【解决方案1】:

    我设法通过切换到命名客户端来完成这项工作:

    //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);
    });
      
    

    【讨论】:

    • 这应该是暂时的还是单例的?
    【解决方案2】:

    如果您不想使用命名客户端,另一种方法是为额外参数创建自定义类。 因为问题是它不知道要为类型字符串解决什么问题,所以您可以做的是创建一个具有字符串属性的对象,该属性将包含您要传递的值,将其注册为单例并让容器解决它。

    创建一个包含所有额外参数的类。在你的情况下“repositoryName”

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

    注册新班级

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

    然后注册HttpClient

    services.AddHttpClient<IGitHubService, GitHubService>();
    

    现在您的类将被正确实例化。

    【讨论】:

      【解决方案3】:

      也许您可以通过HttpClients 属性传递参数。仓库名可以通过BaseAddress传递。

      var repositoryName = // load from a config for example
      
      services.AddHttpClient<GitHubService>(c =>
      {
          c.BaseAddress = new Uri($"https://api.github.com/repos/aspnet/{repositoryName}");
          c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
          c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
      });
      

      【讨论】:

      • 这是个好主意,但不幸的是,对于我的实际用例,参数在查询字符串中,我无法更新我正在调用的服务。我最终使用了命名客户端。感谢您的回复。
      【解决方案4】:

      如果您使用Autofac 作为依赖解析器,您可以使用Delegate Factories 实现此目的。

      它看起来像这样

      public delegate GitHubService GitHubServiceFactory(string repositoryName);
      
      public class AnotherService
      {
          private GitHubService gitHubService;
          public AnotherService(GitHubServiceFactory gitHubServiceFactory)
          {
              this.gitHubService = gitHubServiceFactory("myRepositoryName");
          }
      }
      

      据我所知,使用内置依赖解析器是不可能的。

      【讨论】:

        【解决方案5】:

        正确的做法是使用AddHttpClient&lt;TClient,TImplementation&gt;(Func&lt;HttpClient, IServiceProvider, TImplementation&gt;)extension method:

        services.AddHttpClient<IGitHubService, GitHubService>((client, sp) =>
            // any other constructor dependencies in GitHubService will be filled in
            // by ActivatorUtilities from the provided IServiceProvider
            ActivatorUtilities.CreateInstance<GitHubService>(sp, client, repositoryName)
        );
        

        【讨论】:

          猜你喜欢
          • 2013-10-22
          • 1970-01-01
          • 2014-11-06
          • 2023-03-10
          • 2011-05-07
          • 2014-11-14
          • 2021-07-05
          • 2020-01-06
          • 1970-01-01
          相关资源
          最近更新 更多