在 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。但这不会成为问题,因为您的应用程序将继续重复使用同一个应用程序。