HttpClient - 此实例已启动 [英] HttpClient - This instance has already started

查看:28
本文介绍了HttpClient - 此实例已启动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 api 中使用 http 客户端收到此异常.

<块引用>

执行请求时发生未处理的异常.System.InvalidOperationException: 此实例已启动一个或多个请求.属性只能在发送第一个请求之前修改.

并且我将我的服务注入为

services.AddSingleton()

我认为单身是我最好的下注.我的问题是什么?

我的使用

class ApiClient{私有只读 HttpClient _client;公共 ApiClient(HttpClient 客户端){_client = 客户端;}公共异步任务GetAsync(字符串uri){_client.BaseAddress = new Uri("http://localhost:5001/");_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json");var response = await _client.GetAsync(uri);返回响应;}}

解决方案

这是类的设计 HttpClient .Net 核心源.

这里有趣的方法是CheckDisposedOrStarted().

private void CheckDisposedOrStarted(){检查处理();如果(_operationStarted){抛出新的 InvalidOperationException(SR.net_http_operation_started);}}

现在在设置属性时调用

  1. BaseAddress
  2. 超时
  3. MaxResponseContentBufferSize

因此,如果您计划重用 HttpClient 实例,您应该设置一个预置这 3 个属性的单个实例,并且所有用户都必须不要修改这些属性.

或者,您可以创建工厂或使用简单的 AddTransient(...).请注意,AddScoped 不适合这里,因为您将收到每个请求范围相同的实例.

编辑基本工厂

现在工厂只不过是负责向另一个服务提供实例的服务.这是一个基本的工厂来构建你的 HttpClient 现在意识到这只是最基本的你可以扩展这个工厂来做你想做的事,并预设了 HttpClient 的每个实例>

公共接口 IHttpClientFactory{HttpClient CreateClient();}公共类 HttpClientFactory : IHttpClientFactory{静态字符串 baseAddress = "http://example.com";公共 HttpClient CreateClient(){var client = new HttpClient();SetupClientDefaults(客户端);回访客户;}protected virtual void SetupClientDefaults(HttpClient客户端){client.Timeout = TimeSpan.FromSeconds(30);//设置自己的超时时间.client.BaseAddress = new Uri(baseAddress);}}

现在我为什么使用和接口?这是通过使用依赖注入和 IoC 来完成的,我们可以很容易地交换"应用程序的一部分.现在,我们访问 IHttpClientFactory,而不是尝试访问 HttpClientFactory.

services.AddScoped();

现在在您的类、服务或控制器中,您将请求工厂接口并生成一个实例.

public HomeController(IHttpClientFactory httpClientFactory){_httpClientFactory = httpClientFactory;}只读 IHttpClientFactory _httpClientFactory;公共 IActionResult 索引(){var client = _httpClientFactory.CreateClient();//.... 做你的代码返回视图();}

这里的关键是.

  1. 工厂负责生成客户端实例并管理默认值.
  2. 我们请求的是接口而不是实现.这有助于我们保持组件断开连接,并允许采用更加模块化的设计.
  3. 该服务已注册为 Scoped 实例.单例有它们的用途,但在这种情况下,您更有可能需要一个作用域实例.

<块引用>

范围内的生命周期服务为每个请求创建一次.

I'm getting this exception using http client in my api.

An unhandled exception has occurred while executing the request. System.InvalidOperationException: This instance has already started one or more requests. Properties can only be modified before sending the first request.

and I injected my service as

services.AddSingleton<HttpClient>()

I thought singleton was my best bet. what could be my problem?

edit: my usage

class ApiClient
{
   private readonly HttpClient _client;
   public ApiClient(HttpClient client)
   {
      _client = client;
   }

   public async Task<HttpResponseMessage> GetAsync(string uri)
   {
     _client.BaseAddress = new Uri("http://localhost:5001/");
     _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json");
     var response = await _client.GetAsync(uri);

     return response;
   }
 }

解决方案

This is the design of the class HttpClient .Net Core Source.

The interesting method here is the CheckDisposedOrStarted().

private void CheckDisposedOrStarted()
{
     CheckDisposed();
     if (_operationStarted)
     {
         throw new InvalidOperationException(SR.net_http_operation_started);
     }
}

Now this is called when setting the properties

  1. BaseAddress
  2. Timeout
  3. MaxResponseContentBufferSize

So if you are planning to reuse the HttpClient instance you should setup a single instance that presets those 3 properties and all uses must NOT modify these properties.

Alternativly you can create a factory or use simple AddTransient(...). Note that AddScoped is not best suited here as you will recieve the same instance per request scope.

Edit Basic Factory

Now a factory is nothing more than a service that is responsible for providing an instance to another service. Here is a basic factory to build your HttpClient now realize this is only the most basic you can extend this factory to do as you wish and presetup every instance of the HttpClient

public interface IHttpClientFactory
{
    HttpClient CreateClient();
}

public class HttpClientFactory : IHttpClientFactory
{
    static string baseAddress = "http://example.com";

    public HttpClient CreateClient()
    {
        var client = new HttpClient();
        SetupClientDefaults(client);
        return client;
    }

    protected virtual void SetupClientDefaults(HttpClient client)
    {
        client.Timeout = TimeSpan.FromSeconds(30); //set your own timeout.
        client.BaseAddress = new Uri(baseAddress);
    }
}

Now why did I use and interface? This is done as using dependency injection and IoC we can easily "swap" parts of the application out very easily. Now instead of trying to access the HttpClientFactory we access the IHttpClientFactory.

services.AddScoped<IHttpClientFactory, HttpClientFactory>();

Now in your class, service or controller you would request the factory interface and generate an instance.

public HomeController(IHttpClientFactory httpClientFactory)
{
    _httpClientFactory = httpClientFactory;
}

readonly IHttpClientFactory _httpClientFactory;

public IActionResult Index()
{
    var client = _httpClientFactory.CreateClient();
    //....do your code
    return View();
}

The key here is.

  1. The factory is responsible for generating the client instance and will manage the defaults.
  2. We are requesting the interface not the implementation. This helps us keep our components disconnected and allow for a more modular design.
  3. The service is registered as a Scoped instance. Singletons have their uses but in this case you are more likely to want a scoped instance.

Scoped lifetime services are created once per request.

这篇关于HttpClient - 此实例已启动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆