如何在每个请求中使用不同的凭证重用HttpClient实例

11

我有一个MVC 5应用程序,其中包含一个控制器动作,该动作发出HTTP请求。 为此,我使用HttpClient。

我从其他人(例如这篇博客文章)和我的经验中学到, 同时使用大量的HttpClient实例可能会对整个应用程序以及服务器的性能产生负面影响。

到目前为止,我通过像这样的代码来解决重用HttpClient实例的要求

using (var handler = new HttpClientHandler() { Credentials = new NetworkCredential(getUsername(), getHashedPassword())})
using (var client = new HttpClient(handler))
{
   // Long running loop that makes a different HTTP request
   // on each iteration but using the same HttpClient instance
}

在控制器操作中需要类似的重用,我选择在控制器类中使用HttpClient的静态实例。问题是由于凭据部分依赖于时间戳值(每次调用getHashedPassword()方法会产生不同的密码),因此传递给HttpClientHandler的凭据随每个请求而变化。

由于远程服务器上进行身份验证的方式,我必须在构成请求正文的XML内容中包含密码。

这就是我的问题所在:

如何在调用控制器操作时跨不同调用重复使用相同的HttpClient实例,同时确保它正在使用具有最新凭据的处理程序?

这是否可能实现?

是否可以在将处理程序传递给HttpInstance后对其进行更新?

还是我需要修改客户端对象的DefaultRequestHeaders,根据某些来源的说法,如果该对象被不同线程并发使用,则可能引入微妙的错误?


你有没有意识到,如果你使用一个静态的HttpClient,并且同时发送多个请求,凭据会混乱?而且,所有非静态方法都不是线程安全的。所以这真的不是一个好主意... - Gusman
@Gusman。我对此不太确定。这也是我提问的原因之一。我正在寻找一种以线程安全的方式共享HttpClient的方法。 - Steve S
为了你所需的,你需要一个客户池,类似于线程池的工作方式。你设置了最大客户数,可以创建一个空的或者带有最小客户数的池。当你想要使用一个客户时,你向池请求一个,如果没有剩余的客户,池会创建一个新的;否则,它会从内部列表中返回一个客户。然后,在你完成与客户的工作后,你将其归还给池。如果已达到最大客户数,池会销毁该客户;否则,它会将其存储在内部列表中,准备下次使用。这并不是一件简单的事情。 - Gusman
2
@Gusman回复:“所有非静态方法都不是线程安全的”这是不正确的。"HttpClient应该在应用程序的整个生命周期中只被实例化一次并重复使用"。有几个实例成员是线程安全的。请参考:https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=netframework-4.7.1 - csauve
3
这是一个老问题,但我会把它放在这里以帮助那些需要的人。我认为没有办法在传递给静态实例的HttpClient之后更新处理程序的凭据。虽然线程安全,但更新DefaultRequestHeaders的共享状态也会表现出意外的行为,这已经指出了。使用httpClient.SendAsync(request)并构造请求对象,添加任何所需的凭据到头属性。 - TheRock
考虑使用SocketsHttpClientHandler而不是HttpClientHandler。它不仅在性能上稍微好一些,还暴露了一个名为'Credentials'的属性,您可以在传输之前设置它。请查看https://learn.microsoft.com/en-us/dotnet/api/system.net.http.socketshttphandler?view=net-6.0 - Amogh Sarpotdar
1个回答

0
HttpClientHandler对象是负责处理HttpClient使用的请求的组件。您希望对于每个需要新凭据的请求更新HttpClientHandler。有几种方法可以实现这一点。 您可以通过在每个请求之前创建一个新的HttpClientHandler实例来设置最新的凭据。
using (var handler = new HttpClientHandler())
    using (var client = new HttpClient(handler))
    {
       handler.Credentials = new NetworkCredential(getUsername(), getHashedPassword());
       // HttpClient örneğini kullanarak istekleri gerçekleştirin
    }

通过更新DefaultRequestHeaders,您可以在每个请求中传递新的凭据。

using (var handler = new HttpClientHandler())
using (var client = new HttpClient(handler))
{
   client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{getUsername()}:{getHashedPassword()}")));
   // HttpClient örneğini kullanarak istekleri gerçekleştirin
}

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