【问题标题】:Multiple httpclient instances with same implementation in dotnet core在 dotnet 核心中具有相同实现的多个 httpclient 实例
【发布时间】:2020-08-10 04:40:03
【问题描述】:

我想调用一个第三方 API,它为我们提供了两个不同的授权令牌值。因此,我们通过调用相同的端点 URL 得到了两组不同的结果。

EndpointUrl: https://mail.yahoo.com/ 
Authorization: Token User123
//Do something with the response for User123
Authorization: Token User345
//Do something with the response for User345

在我的客户端服务中,我的包装函数应该通过使用不同的令牌值调用它两次来调用此 API。获取结果并合并。

这是我的服务。

public class MailService : IMailService
{
    private readonly HttpClient _httpClient;
    
    public MailService(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<UserResponse> GetUserResponse()
    {
        var uri = new Uri(_httpClient.BaseAddress.AbsoluteUri + "/user-data/");
        var response = await _httpClient.GetAsync(uri);
        return response;
    }
}

我使用的是Typed Client

services.AddHttpClient<IMailService,MailService>(client =>
{
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", "User123");                
    client.BaseAddress = new Uri("https://mail.yahoo.com/");
})

问题陈述:

检索结果的最佳方法是什么?我不确定是否应该创建两个单独的HttpClient 实例?如果我使用两个包含它们自己的 HttpClient 的不同类,我想避免重复代码。

也许我的解决方案在Named Client 的某个地方。我只是不知道如何优雅地实现它。

任何帮助将不胜感激。

【问题讨论】:

  • 我已经发布了几个 cmets 然后删除,因为这是一种奇怪的做法 IHttpClientFactory 以前从未见过它。我刚刚打开 VS 2019 启动了一个新的 .NETCore 3.1 WebAPI,但我无法让它运行。将HttpClient 注入到服务中使我的代码非常适合。这对你来说很好吗?你从构造函数中获得了 IMailService 中的 HttpClient?
  • @Andy 完全有效,查找“Typed HttpClient”
  • 很奇怪。无法让它按照你的方式工作。我早上不那么累的时候试试。

标签: c# asp.net-core httpclient


【解决方案1】:

检索结果的最佳方法是什么?

命名客户端是一种解决方案,如果您确定您将始终拥有两个身份验证标头。不过,这对我来说似乎很奇怪。

SendAsync 是另一种解决方案。通过调用SendAsync 而不是GetAsync,您可以提供可以具有自定义标头的HttpRequestMessage。为方便起见,GetAsync 本质上是对SendAsync 的包装,您可以使用较低级别的SendAsync,因为您需要更多自定义。

【讨论】:

  • 我没有两个身份验证标头,只是您添加了两个可能的标头值,它们会给出不同的结果集。 httpclient 的所有其他配置将保持不变。
  • 对,我就是这个意思。有 1 个或 N 个不同的标记是正常的,但正好 2 个是不寻常的。
【解决方案2】:

我会回答,这或多或少是基于意见的。我喜欢将我的 DI 服务与项目中的其他所有内容分离。基本上,您是在 startup.cs 中为另一个服务配置配置。我喜欢将所有这些内容保留在使用 HttpClient 的服务中。

所以当我注入IHttpClientFactory 时,我只需调用:

services.AddHttpClient();

然后继续。

现在,在您的 MailService 中,您可以这样注入它:

public class MailService : IMailService
{
    // or store this in your applications external configuration
    const Uri BaseUri = new Uri("https://mail.yahoo.com/");
    const string UserDataPath = "/user-data/";
    
    private readonly IHttpClientFactory _httpClientFactory;
    
    public MailService(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    public async Task<UserResponse> GetUserResponse(string token)
    {
        var client = _httpClientFactory.CreateClient();
        
        var uri = new UriBuilder(BaseUri){ Path = UserDataPath }.Uri;
        
        using (var msg = new HttpRequestMessage(HttpMethod.Get, uri))
        {
            msg.Headers.Authorization = new AuthenticationHeaderValue("Token", token); 
            using (var resp = await _client.SendAsync(msg).ConfigureAwait(false))
            {
                resp.EnsureSuccessStatusCode();
                return await resp.Content.ReadAsAsync<UserResponse>().ConfigureAwait(false);
            }
        }
    }
}

请记住,您应该使用HttpRequestMessage 模式,而不是修改HttpClient 的任何Default... 属性。那时它可能在您的应用程序的其他地方使用。

使用HttpRequestMessage 可确保您获得一组其他人无法修改的新标头。总结一下:HttpClient.Headersare not thread-safe

预计到达时间:

现在我想起来了……这确实是你的问题。您想通过一个HttpClient 来使用两个不同的标头。它不适用于您提出的方案。您必须有两个 HttpClient 有各自不同的标头。

因此,如果您不想按照我介绍的老式方式进行操作,您应该考虑“命名”客户端...每个配置都有不同的名称。

【讨论】:

  • 所以,根据您的建议,我最好为两个httpclient 实例调用两次_mailService.GetUserResponse()。对吗?
  • @Shaggy -- 是的。抱歉,回答过于夸张。已经很晚了,我太累了。问题是,如果您为控制器创建单个客户端,但希望它使用自己的标头执行不同的请求,它将无法正常工作。您将获得竞争条件,因为每个请求都将竞争该特定标头。如果您同时执行两个请求并且它们都尝试同时修改标头,您甚至可能会崩溃。因此,您要么需要使用“命名”客户端,要么需要我在答案中显示的方式。 “类型化”的客户端在这里不起作用。
  • @Shaggy -- 我更新了我的答案以更改GetUserResponse 上的签名以将令牌传递给它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-12
  • 1970-01-01
  • 1970-01-01
  • 2013-11-07
相关资源
最近更新 更多