Httpclient This instance has already started one o

2020-06-07 07:04发布

问题:

I am creating an application in .Net Core 2.1 and I am using http client for web requests. The issue is I have to send parallel calls to save time and for that I am using Task.WhenAll() method but when I hit this method I get the error "This instance has already started one or more requests. Properties can only be modified before sending the first request" Previously I was using RestSharp and everything was fine but I want to use httpclient. Here is the code:

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)
    { ....

Here is the constructor:

private readonly HttpClient _httpClient;

public UserRepository(HttpClient httpClient)
{         
    _httpClient = httpClient;
}

Method calling:

var user1 = AddUser("user@user.com");
var user2 = AddUser("test@test.com");

await Task.WhenAll(user1, user2);

and here is the startup configuation:

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

So what am I doing wrong? Do I need to change AddSingleton with AddTransient() or is there any other issue. One more question do I need to use _client.Dispose() after the response because the tutorial which I followed didn't use dispose method so I am little confused in that.

回答1:

HttpClient.DefaultRequestHeaders (and BaseAddress) should only be set once, before you make any requests. HttpClient is only safe to use as a singleton if you don't modify it once it's in use.

Rather than setting DefaultRequestHeaders, set the headers on each HttpRequestMessage you are sending.

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);

Replace "{...}" with your JSON.



回答2:

Maybe my two cents will help someone.

I ran into this issue when refreshing the page when debugging the application.

I was using a singleton, but each refresh, it was trying to set the base address. So I just wrapped it in a check to see if the base address had already been set.

The issue for me was, it was trying to set the baseAddress, even though it was already set. You can't do this with a httpClient.

if (_httpClient.BaseAddress == null)
{
    _httpClient.BaseAddress = new Uri(baseAddress);
}


回答3:

I solved my case removing the constructor of the class and passed to use the pattern Idisposable with Using for the class HttpClient, for example:

public async Task<User> AddUser(string email)
{

    var url = "user/";
    using (HttpClient client = new HttpClient())
    {
        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)
        { ...

        }
    }
}