具有不同身份验证标头的 HttpClient 单个实例

Posted

技术标签:

【中文标题】具有不同身份验证标头的 HttpClient 单个实例【英文标题】:HttpClient single instance with different authentication headers 【发布时间】:2016-10-22 01:32:57 【问题描述】:

鉴于 .net HttpClient 在设计时考虑了重用性,并且在短期实例中旨在成为 long lived 和 memory leaks have been reported。在为多个用户调用端点时,您希望使用不同的承载令牌(或任何授权标头)对给定端点进行安静的调用,有哪些指导方针?

private void CallEndpoint(string resourceId, string bearerToken) 
  httpClient.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("bearer", bearerToken);
  var response = await httpClient.GetAsync($"resource/resourceid");

鉴于上述代码可以被 Web 应用程序上的任意数量的线程调用,很可能第一行中设置的标头与调用资源时使用的标头不同。

在不使用锁和维护无状态 Web 应用程序的情况下,为单个端点创建和处理 HttpClients 的推荐方法是什么(我目前的做法是为每个端点创建一个客户端)?


生命周期

虽然 HttpClient 确实间接实现了 IDisposable 接口,HttpClient的推荐用法是不要dispose 在每个请求之后。 HttpClient 对象旨在为 只要您的应用程序需要发出 HTTP 请求。有一个对象 存在于多个请求中启用设置的地方 DefaultRequestHeaders 并防止您必须重新指定内容 就像每个请求上的 CredentialCache 和 CookieContainer 一样 HttpWebRequest 是必需的。

【问题讨论】:

您是在谈论相对较少的不同身份验证标头或批次,例如每个用户都是唯一的吗? @ToddMenier - 这将是每个用户的唯一标题。这将是用户 oauth 令牌。我认为 scott hannen 让我走上了正轨。看起来一些扩展方法是有序的。 你好@Bronumski,你能分享你解决这个问题的方法吗?我在添加相同标题但内容不同的多个线程时遇到相同的问题。 @LuisMejia - 我已经更新了 scotts 的答案,并附上了我如何进行 GET 和 POST 的示例。您想要实现的任何其他方法都使用相同的原则。扩展方法包含一个操作,允许您在发送 HttpRequest 之前对其进行操作。 @Bronumski 感谢您的回答...似乎我正在采用类似的方式使用 sendasync 并将请求作为参数与自定义标头一起传递。 【参考方案1】:

如果您的标题通常是相同的,那么您可以设置DefaultRequestHeaders。但是您不需要使用该属性来指定标题。正如您已经确定的那样,如果您要使用同一个客户端有多个线程,那将是行不通的。在一个线程上更改默认标头会影响在其他线程上发送的请求。

虽然您可以在客户端上设置默认标头并将其应用于每个请求,但标头实际上是请求的属性。因此,当标头特定于请求时,您只需将它们添加到请求中即可。

request.Headers.Authorization = new AuthenticationHeaderValue("bearer", bearerToken);

这意味着您不能使用不涉及创建HttpRequest 的简化方法。您需要使用

public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)

记录在here。


有些人发现使用扩展方法将更新标头的代码与方法的其余部分隔离开来很有帮助。

通过扩展方法完成的 GET 和 POST 方法示例,允许您在发送之前操纵请求标头和更多 HttpRequestMessage

public static Task<HttpResponseMessage> GetAsync
    (this HttpClient httpClient, string uri, Action<HttpRequestMessage> preAction)

    var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, uri);

    preAction(httpRequestMessage);

    return httpClient.SendAsync(httpRequestMessage);


public static Task<HttpResponseMessage> PostAsJsonAsync<T>
    (this HttpClient httpClient, string uri, T value, Action<HttpRequestMessage> preAction)

    var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, uri)
    
        Content = new ObjectContent<T>
            (value, new JsonMediaTypeFormatter(), (MediaTypeHeaderValue)null)
    ;
    preAction(httpRequestMessage);

    return httpClient.SendAsync(httpRequestMessage);

这些可以像下面这样使用:

var response = await httpClient.GetAsync("token",
    x => x.Headers.Authorization = new AuthenticationHeaderValue("basic", clientSecret));

【讨论】:

很好的解决方案。刚刚实施。在 public static Task GetAsync(this HttpClient httpClient, string uri, string token, CancellationToken cancelToken) return httpClient.GetAsync(uri, x => SetToken(x, token), cancelToken); void SetToken(HttpRequestMessage request, string token, string type = "Bearer") HttpClientHandler.ProxyHttpClientHandler.CookieContainerHttpClientHandler的其他属性不能在HttpRequestMessage中设置怎么办? (或者他们可以吗?) @DavidS。你找到代理的解决方案了吗? 对于代理、cookiecontainer 等,如果您对单个请求有特定需求,我相信建议使用具有您对这些特定请求所需的配置的命名或类型客户端,然后您可以使用所有其他人的未命名客户。 docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests @hemp - 我同意,但该文档适用于 Microsoft.Extensions.DependencyInjection (IServiceCollection),或者至少我们不太清楚我们如何将它与不同的 IoC 容器一起使用或没有。

以上是关于具有不同身份验证标头的 HttpClient 单个实例的主要内容,如果未能解决你的问题,请参考以下文章

如何在同一个 Angular JS 应用程序中使用具有不同身份验证标头的不同 API

具有摘要身份验证的 HttpClient 发布请求导致错误请求

具有身份验证的 Apache HttpClient Socks5 代理

IBM Worklight 6.0 - 如果客户端注销/登录,具有基本身份验证的适配器不会更新身份验证标头

不同“接受”内容类型标头字段的不同身份验证

如何在具有肥皂身份验证标头的 Netbeans 中使用 .NET Web 服务