在 using 块中使用静态 HttpClient 是 viable/thread-safe 吗?
Is it viable/thread-safe to use a static HttpClient in a using block?
假设我有一个带有 Index()
方法的控制器,并且该控制器利用多个 "Manager classes" 来管理需要使用 HttpClient
从 [=32] 中检索的某些资产=].
我读到与多个调用共享 HttpClient
比每次调用都重新实例化它以保存端口要好。
但是我确实想在控制器 returns 视图之前处理 HttpClient
,因为视图包含一个完整的基于 Knockout/Typescript 的前端项目来处理其余的数据(所以它基本上只有设置和元数据的东西)。
我是否需要将 HttpClient
变量传递给每个 "Manager class",或者是否足以执行以下操作,并在 类?
public ActionResult Index()
{
using (Globals.Client = new System.Net.Http.HttpClient())
{
// do stuff like SettingManager.GetSetting("settingKey") which uses
// the Globals.Client variable
}
return View();
}
或者我根本不想处理 HttpClient
?
一个解决方案是创建一个单独的依赖项来负责管理您的 HttpClient
。这有一个附带的好处,就是让你的控制器不直接依赖于 HttpClient
。任何依赖于 HttpClient
的 class 变得更难测试。这也是一个维护问题,因为如果你想改变行为,你必须在任何地方都改变它。想象一下,如果有一天您决定可以缓存从 HttpClient
中获得的任何内容?您必须更改很多 classes。
您可以像这样定义抽象和实现:
public interface IDoesSomething
{
string GetSetting(string key);
}
public class HttpClientDoesSomething : IDoesSomething, IDisposable
{
private readonly HttpClient _client;
private readonly string _apiUrl;
public HttpClientDoesSomething(string apiUrl)
{
_client = new HttpClient();
_apiUrl = apiUrl;
}
public string GetSetting(string key)
{
// use the client to retrieve the setting
}
public void Dispose()
{
_client?.Dispose();
}
}
现在问题已从您的控制器中移出,因为您注入了接口:
public class MyController : Controller
{
private readonly IDoesSomething _doesSomething;
public MyController(IDoesSomething doesSomething)
{
_doesSomething = doesSomething;
}
public ActionResult Index()
{
var setting = _doesSomething.GetSetting("whatever");
// whatever else this does.
return View();
}
}
现在在您的启动配置中,您可以将 HttpClientDoesSomething
注册为单例:
services.AddSingleton<IDoesSomething>(new HttpClientDoesSomething("url from settings"));
您的实现是一次性的,因此如果您确实需要创建和处置它,您也将处置 HttpClient
。但这不会成为问题,因为您的应用程序将继续重复使用同一个应用程序。
假设我有一个带有 Index()
方法的控制器,并且该控制器利用多个 "Manager classes" 来管理需要使用 HttpClient
从 [=32] 中检索的某些资产=].
我读到与多个调用共享 HttpClient
比每次调用都重新实例化它以保存端口要好。
但是我确实想在控制器 returns 视图之前处理 HttpClient
,因为视图包含一个完整的基于 Knockout/Typescript 的前端项目来处理其余的数据(所以它基本上只有设置和元数据的东西)。
我是否需要将 HttpClient
变量传递给每个 "Manager class",或者是否足以执行以下操作,并在 类?
public ActionResult Index()
{
using (Globals.Client = new System.Net.Http.HttpClient())
{
// do stuff like SettingManager.GetSetting("settingKey") which uses
// the Globals.Client variable
}
return View();
}
或者我根本不想处理 HttpClient
?
一个解决方案是创建一个单独的依赖项来负责管理您的 HttpClient
。这有一个附带的好处,就是让你的控制器不直接依赖于 HttpClient
。任何依赖于 HttpClient
的 class 变得更难测试。这也是一个维护问题,因为如果你想改变行为,你必须在任何地方都改变它。想象一下,如果有一天您决定可以缓存从 HttpClient
中获得的任何内容?您必须更改很多 classes。
您可以像这样定义抽象和实现:
public interface IDoesSomething
{
string GetSetting(string key);
}
public class HttpClientDoesSomething : IDoesSomething, IDisposable
{
private readonly HttpClient _client;
private readonly string _apiUrl;
public HttpClientDoesSomething(string apiUrl)
{
_client = new HttpClient();
_apiUrl = apiUrl;
}
public string GetSetting(string key)
{
// use the client to retrieve the setting
}
public void Dispose()
{
_client?.Dispose();
}
}
现在问题已从您的控制器中移出,因为您注入了接口:
public class MyController : Controller
{
private readonly IDoesSomething _doesSomething;
public MyController(IDoesSomething doesSomething)
{
_doesSomething = doesSomething;
}
public ActionResult Index()
{
var setting = _doesSomething.GetSetting("whatever");
// whatever else this does.
return View();
}
}
现在在您的启动配置中,您可以将 HttpClientDoesSomething
注册为单例:
services.AddSingleton<IDoesSomething>(new HttpClientDoesSomething("url from settings"));
您的实现是一次性的,因此如果您确实需要创建和处置它,您也将处置 HttpClient
。但这不会成为问题,因为您的应用程序将继续重复使用同一个应用程序。