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

鉴于。Net HttpClient 在设计时考虑到了重用,并且在短期实例中设计为 长命百岁内存泄漏已被报告。在为多个用户调用端点时,您希望使用不同的承载标记(或任何授权标头)对给定端点进行 restful 调用时,有哪些指导原则?

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 不要释放它 HttpClient 对象的目的是作为 只要应用程序需要发出 HTTP 请求 存在于多个请求之间,从而为设置 DefaultRequestHeader 并防止您必须重新指定事物 就像以前一样,对每个请求都使用 CredentialCache 和 CookieContainer 使用 HttpWebRequest 所必需的。

22583 次浏览

If your headers are usually going to be the same then you can set the DefaultRequestHeaders. But you don't need to use that property to specify headers. As you've determined, that just wouldn't work if you're going to have multiple threads using the same client. Changes to the default headers made on one thread would impact requests sent on other threads.

Although you can set default headers on the client and apply them to each request, the headers are really properties of the request. So when the headers are specific to a request, you would just add them to the request.

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

That means you can't use the simplified methods that don't involve creating an HttpRequest. You'll need to use

public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)

documented here.


Some have found it helpful to use extension methods to isolate the code that updates the headers from the rest of a method.

Example of GET and POST methods done through an extension method that allow you to manipulate the request header and more of the HttpRequestMessage before it is sent:

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

These could then be used like the following:

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