我已经阅读了很多关于如何尽可能多地重用HttpClient实例的内容,甚至可能在整个应用程序生命周期中。为了完整起见,这里有几个我基于我的陈述的资源:
我对此有几个问题:
到目前为止,这是我正在进行的代码的样子(为了简洁起见,在某种简化的形式中):
public class MyHttpClientWrapper : IDisposable
{
private readonly HttpClient _httpClient;
private readonly TokenManager _tokenManager;
public HttpServiceClient(HttpClient httpClient, TokenManager tokenManager)
{
_httpClient = httpClient;
_tokenManager = tokenManager;
_httpClient.BaseAddress = new Uri("https://someapp/api/");
_httpClient.DefaultRequestHeaders.Accept.Add(new
MediaTypeWithQualityHeaderValue("application/json"));
}
public string GetDataByQuery(string query)
{
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"amx", _tokenManager.GetNewAuthorizationCode());
var response = _httpClient.GetAsync(query).Result;
return response.Content.ReadAsStringAsync().Result;
}
public void Dispose()
{
HttpClient?.Dispose();
}
}
附注:我在这里使用依赖注入,但不一定是IoC容器(由于与此讨论无关的原因)。
答案 0 :(得分:0)
在网上进行了一些额外的阅读后,我想出了这个:
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封装在一个属性中,并使其成为非readonly。这样,httpclient可以通过属性设置器从单元测试中覆盖。我不确定我是否喜欢这个解决方案。
另一个想法是通过属性对静态_httpClient进行延迟初始化,但我不确定这是否更好。
对这些想法中的任何一个有何看法?还有其他想法吗?
答案 1 :(得分:0)
我决定稍微改变一下,以便我可以进行单元测试。我在这里使用属性注入来允许在单元测试中覆盖Http客户端。但是在生产代码中,它只会在第一次访问Client属性时自行初始化(懒惰)。
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;
}
}