Httpclient 该实例已经启动了一个或多个请求。只能在发送第一个请求之前修改属性
Posted
技术标签:
【中文标题】Httpclient 该实例已经启动了一个或多个请求。只能在发送第一个请求之前修改属性【英文标题】:Httpclient This instance has already started one or more requests. Properties can only be modified before sending the first request 【发布时间】:2018-07-23 12:13:04 【问题描述】:我正在 .Net Core 2.1 中创建一个应用程序,并且我正在使用 http 客户端进行 Web 请求。问题是我必须发送并行调用以节省时间,为此我正在使用 Task.WhenAll() 方法,但是当我点击此方法时,我收到错误“此实例已启动一个或多个请求。只能修改属性在发送第一个请求之前”以前我使用的是 RestSharp,一切都很好,但我想使用 httpclient.代码如下:
public async Task<User> AddUser(string email)
var url = "user/";
_client.BaseAddress = new Uri("https://myWeb.com/");
_client.DefaultRequestHeaders.Accept.Clear();
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(Constants."application/json"));
_client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
var json = new email = email
var response = await _client.PostAsJsonAsync(url,json);
if (response .IsSuccessStatusCode)
....
这里是构造函数:
private readonly HttpClient _httpClient;
public UserRepository(HttpClient httpClient)
_httpClient = httpClient;
方法调用:
var user1 = AddUser("user@user.com");
var user2 = AddUser("test@test.com");
await Task.WhenAll(user1, user2);
这里是启动配置:
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
那么我做错了什么?我需要用 AddTransient() 更改 AddSingleton 还是有任何其他问题。还有一个问题,我需要在响应后使用 _client.Dispose() 吗,因为我遵循的教程没有使用 dispose 方法,所以我对此有点困惑。
【问题讨论】:
每次调用都不要更改基地址和默认标头 我应该在启动时添加基址吗? 你的单身人士是<IHttpContextAccessor, HttpContextAccessor>
而不是 <HttpClient>
有什么原因吗?
是的,这样我得到了这个错误:“尝试激活'时无法解析类型''的服务”
您可能想查看Flurl。它使从请求级标头到不记名令牌再到 HttpClient 单例管理的一切都变得更加容易。
【参考方案1】:
HttpClient.DefaultRequestHeaders
(和BaseAddress
)在您提出任何请求之前只能设置一次。 HttpClient
只有在使用后不对其进行修改才能安全地用作单例。
不要设置DefaultRequestHeaders
,而是在您发送的每个HttpRequestMessage
上设置标头。
var request = new HttpRequestMessage(HttpMethod.Post, url);
request.Headers.Accept.Clear();
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
request.Content = new StringContent("...", Encoding.UTF8, "application/json");
var response = await _client.SendAsync(request, CancellationToken.None);
用您的 JSON 替换 "..."
。
【讨论】:
这一切都很好,但我真的被 Cookie 困住了。它们在访问 API 时并不常见,但有时在抓取时需要。因此,我想控制 cookie 容器并将其设置为带有一些请求的新容器。如果我们想重用 HttpClient,这似乎是不可能的 @GarrGodfrey 可以按请求禁用 cookie,但是为一组请求使用单独的 cookie 容器需要单独的HttpClient
和单独的 SocketsHttpHandler
,因此您将无法在这种情况下无论如何都要使用单例HttpClient
。您可以将其与IHttpClientFactory
合并。如果您想获得更详细的答案,请提出单独的问题。
首次使用后更改UseCookies
的值失败,与更改BaseAddress
的错误相同。 HttpClient 可能只是麻烦多于它的价值。【参考方案2】:
也许我的两分钱会帮助某人。
我在调试应用程序时刷新页面时遇到了这个问题。
我使用的是单例,但每次刷新时,它都试图设置基地址。所以我只是把它包装在一个检查中,看看是否已经设置了基地址。
我的问题是,它试图设置 baseAddress,即使它已经设置好了。你不能用 httpClient 来做到这一点。
if (_httpClient.BaseAddress == null)
_httpClient.BaseAddress = new Uri(baseAddress);
【讨论】:
这实际上帮助了我很多,这正是我所面临的。【参考方案3】:此问题是由为同一 httpclient 实例重置 BaseAddress 和标头引起的。
我试过了
if (_httpClient.BaseAddress == null)
但我并不热衷于此。
在我看来,更好的解决方案是使用 httpclientFactory。这将终止并在 httpclient 使用后对其实例进行垃圾收集。
private readonly IHttpClientFactory _httpClientFactory;
public Foo (IHttpClientFactory httpClientFactory)
_httpClientFactory = httpClientFactory;
public httpresponse Bar ()
_httpClient = _httpClientFactory.CreateClient(command.ClientId);
using var response = await _httpclient.PostAsync(uri,content);
return response;
// here as there is no more reference to the _httpclient, the garbage collector will clean
// up the _httpclient and release that instance. Next time the method is called a new
// instance of the _httpclient is created
【讨论】:
喜欢这里的工厂。应该注意它需要依赖注入,这可能不适合所有用例。【参考方案4】:当您在消息中添加请求 url 和标头时效果很好,而不是在客户端。所以最好不要分配给BaseAddress
或标头DefaultRequestHeaders
,如果您将它们用于许多请求。
HttpRequestMessage msg = new HttpRequestMessage
Method = HttpMethod.Put,
RequestUri = new Uri(url),
Headers = httpRequestHeaders;
;
httpClient.SendAsync(msg);
【讨论】:
以上是关于Httpclient 该实例已经启动了一个或多个请求。只能在发送第一个请求之前修改属性的主要内容,如果未能解决你的问题,请参考以下文章