C# 当每个请求都需要新的授权头时,在web应用程序中重用HttpClient
我已经读了很多关于如何尽可能多地重用HttpClient实例的文章,甚至可能是在整个应用程序生命周期中。为了完整起见,以下是我的陈述所基于的几个资源:C# 当每个请求都需要新的授权头时,在web应用程序中重用HttpClient,c#,asp.net-mvc,http-headers,dotnet-httpclient,C#,Asp.net Mvc,Http Headers,Dotnet Httpclient,我已经读了很多关于如何尽可能多地重用HttpClient实例的文章,甚至可能是在整个应用程序生命周期中。为了完整起见,以下是我的陈述所基于的几个资源: 关于这一点,我有几个问题: 如何在ASP.NET MVC中创建一个应用程序范围的HttpClient实例,以便在所有请求之间共享?让我们假设图片中并没有IoC容器,所以我不能在这里用容器名称将它绑定到Singleton范围中,然后就此结束。我将如何“手动执行” 此外,我正在与之交互的web服务在每个请求上都需要一个新的授权令牌,因此,即
旁注:我在这里使用依赖注入,但不一定是一个IoC容器(出于与本讨论无关的原因)。在在线阅读了一些其他内容后,我提出了以下建议:
public class MyHttpClientWrapper
{
private static readonly HttpClient _httpClient;
private readonly TokenManager _tokenManager;
static MyHttpClientWrapper()
{
// Initialize the static http client:
_httpClient = new HttpClient();
_httpClient.BaseAddress = new Uri("https://someapp/api/");
_httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
public HttpServiceClient(TokenManager tokenManager)
{
_tokenManager = tokenManager;
}
public string GetDataByQuery(string query)
{
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"amx", _tokenManager.GetNewAuthorizationCode());
var response = _httpClient.GetAsync(query).Result;
return response.Content.ReadAsStringAsync().Result;
}
}
唯一的问题是,它不是单元可测试的。我没有办法用一个假的http客户端替换它。我可以将httpClient封装在一个属性中,使其成为非只读的。这样,可以通过属性设置器从单元测试覆盖httpclient。我不确定我是否喜欢这个解决方案
另一个想法是通过属性对static_httpClient进行延迟初始化,但我不确定这是否更好
对这两种想法有什么想法吗?还有其他想法吗?我决定稍微改变一下,这样我就可以进行单元测试了。我在这里使用属性注入来允许在单元测试中重写Http客户机。但在生产代码中,它只需在第一次访问客户机属性时进行自初始化(惰性)
public class MyHttpClientWrapper
{
private static readonly object ThreadLock = new object();
private static HttpClient _httpClient;
private readonly TokenManager _tokenManager;
public Client
{
get
{
if (_httpClient != null) return _httpClient;
// Initialize http client for the first time, and lock for thread-safety
lock (ThreadLock)
{
// Double check
if (_httpClient != null) return _httpClient;
_httpClient = new HttpClient();
InitClient(_httpClient);
return _httpClient;
}
}
set
{
// primarily used for unit-testing
_httpClient = value;
InitClient(_httpClient);
}
}
private void InitClient(HttpClient httpClient)
{
httpClient.BaseAddress = new Uri("https://someapp/api/");
httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
public HttpServiceClient(TokenManager tokenManager)
{
_tokenManager = tokenManager;
}
public string GetDataByQuery(string query)
{
Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"amx", _tokenManager.GetNewAuthorizationCode());
var response = _httpClient.GetAsync(query).Result;
return response.Content.ReadAsStringAsync().Result;
}
}
假设您的应用程序是异步的,如果多个线程进入GetDataByQuery,并且它们都更改了DefaultRequestHeaders.Authorization,会发生什么情况?我相信您可以创建一个HttpRequestMessage对象,在其上设置授权头,最后调用_httpClient.SendAsync(requestMessage)是的,这一点很好。事实上,我在某一点上也意识到了同样的事情,并开始做你在那里描述的事情。
public class MyHttpClientWrapper
{
private static readonly object ThreadLock = new object();
private static HttpClient _httpClient;
private readonly TokenManager _tokenManager;
public Client
{
get
{
if (_httpClient != null) return _httpClient;
// Initialize http client for the first time, and lock for thread-safety
lock (ThreadLock)
{
// Double check
if (_httpClient != null) return _httpClient;
_httpClient = new HttpClient();
InitClient(_httpClient);
return _httpClient;
}
}
set
{
// primarily used for unit-testing
_httpClient = value;
InitClient(_httpClient);
}
}
private void InitClient(HttpClient httpClient)
{
httpClient.BaseAddress = new Uri("https://someapp/api/");
httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
public HttpServiceClient(TokenManager tokenManager)
{
_tokenManager = tokenManager;
}
public string GetDataByQuery(string query)
{
Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"amx", _tokenManager.GetNewAuthorizationCode());
var response = _httpClient.GetAsync(query).Result;
return response.Content.ReadAsStringAsync().Result;
}
}