C# HttpClient 自定义请求头每个请求

3
我注意到在修改HttpClient.DefaultRequestHeaders时,使用HttpClient并不线程安全,但我希望尽可能多地发出请求。每个请求都需要自定义标头(另外两个标头始终相同)。此外,URL会略有变化。
目前,我为每个请求创建一个新的HttpClient,但是我觉得创建10k+ HttpClients并不是最好的选择。
我想创建一个具有2个DefaultRequestHeaders的静态HttpClient,并将其用于每个请求,但也要添加一个自定义标头。
我希望尽可能快地完成这个过程,所以如果您有其他方法,请告诉我。
        Parallel.ForEach(Requests, Request =>
        {
            var Client = new HttpClient();
            Client.DefaultRequestHeaders.Clear();
            Client.DefaultRequestHeaders.Add("Header1", "Value1");
            Client.DefaultRequestHeaders.Add("Header2", "Value2");
            Client.DefaultRequestHeaders.Add("Header3", "Value for exact this request");

            var response = Client.PutAsync(new Uri($"http://example.com/books/1234/readers/837"), null); //.Result (?)
            Client.Dispose();
        });
3个回答

10
不要使用DefaultRequestHeaders来设置不适用于HttpClient发送的所有请求的标头。
此外,不要为每个请求创建一个HttpClient。
可以通过为每个请求创建一个HttpRequestMessage,并将所需的任何标头应用到它上面,然后始终使用相同的HttpClient来.SendAsync()它们来轻松实现此操作:
using (var request = new HttpRequestMessage(HttpMethod.Put, url)
{
    request.Headers.<add here>;
    // optionally set .Content

    using (var response = await httpClient.SendAsync(request))
    {
        // ... process response
    }
}

此外,DefaultRequestHeaders 不是线程安全的,如果您关心并行性,就不应该使用它:https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient.defaultrequestheaders?view=net-5.0#remarks - Psddp
使用 HttpRequestMessage 的方式看起来很棒!但是我们需要第二个 "using" 吗? - w2000

2
我认为使用DelegatingHandler可以实现您想要的功能。您需要停止使用DefaultRequestHeaders,而是在DelegatingHandler中设置它们。
此外,请注意,对于请求,HttpClient是线程安全的,不应该被处理掉。这是因为它会处理底层的HttpMessageHandler,从而导致一些较低级别的效率问题。更多细节请参考:http://www.nimaara.com/2016/11/01/beware-of-the-net-httpclient/Sample.cs
// .Net Framework, careful here because the sockets will be cached, network changes will break your app
// see http://www.nimaara.com/2016/11/01/beware-of-the-net-httpclient/ for more details

var client = new HttpClient(new MyCustomHandler() { InnerHandler = new HttpClientHandler() });

// .Net Core
client = new HttpClient(new MyCustomHandler() { InnerHandler = new SocketsHttpHandler() { PooledConnectionLifetime = TimeSpan.FromMinutes(1) } });

Parallel.ForEach(Requests, Request =>
{

    var response = client.PutAsync(new Uri($"http://example.com/books/1234/readers/837"), null); //.Result (?)

    // do not dispose of httpclient!
});

MyCustomHandler.cs

public class MyCustomHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // the static headers
        request.Headers.Add("Header1", "Value1");
        request.Headers.Add("Header2", "Value2");

        // the unique header
        SignRequest(request);

        return base.SendAsync(request, cancellationToken);
    }


    public void SignRequest(HttpRequestMessage message)
    {
        // usually the pattern of a unique header per request is for an authorization header based on some signature of the request
        // this logic would be here
        // generate the signature
        string signature = new Random().Next(int.MaxValue).ToString();

        message.Headers.Add("Header3", signature);
    }
}

2
.NET Core 2.1以后使用HttpClient的标准场景是: 下面是一个直接传递所有标头且不进行批处理的简单示例:
var tasks = Enumerable.Range(1, 10000)
    .Select(v => HttpClientFactory.Create().SendAsync(new HttpRequestMessage(HttpMethod.Put, new Uri($"http://example.com/books/1234/readers/837"))
    {
        Headers =
        {
            {"Header1", "Value1"},
            {"Header2", "Value2"},
            {"Header3", v.ToString()},
        },
        Content = new StringContent("{test:\"hello\"}", Encoding.UTF8, "application/json")
    }));

var responses = await Task.WhenAll(tasks).ConfigureAwait(false);
// ..

备注:

  • 发送成千上万个请求看起来很可疑,您确定服务器没有用于批量处理项的端点
  • 对于非CPU消耗任务使用PLINQ不正确(引用:“PLINQ查询根据主机计算机的能力在并发度上进行扩展”)
原始答案:Original Answer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接