具有不同身份验证的 HttpClient 单实例 headers

HttpClient single instance with different authentication headers

考虑到 .net HttpClient 在设计时考虑了重用,并且旨在 long lived and memory leaks have been reported 在短期实例中。在为多个用户调用端点时,您希望使用不同的承载令牌(或任何授权 header)对给定端点进行 restful 调用的指导原则是什么?

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

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

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


Lifecycle

Although HttpClient does indirectly implement the IDisposable interface, the recommended usage of HttpClient is not to dispose of it after every request. The HttpClient object is intended to live for as long as your application needs to make HTTP requests. Having an object exist across multiple requests enables a place for setting DefaultRequestHeaders and prevents you from having to respecify things like CredentialCache and CookieContainer on every request, as was necessary with HttpWebRequest.

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

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

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

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

public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)

已记录 here


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

通过扩展方法完成的 GET 和 POST 方法示例,允许您在发送请求之前操纵请求 header 和更多 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));