使用HttpClient的最佳实践

16

我正在使用HttpClient向WebApi发出请求。

这是我的代码:

 public async Task<string> ExecuteGetHttp(string url, Dictionary<string, string> headers = null)
        {
            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri(url);
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                if (headers != null)
                {
                    foreach (var header in headers)
                    {
                        client.DefaultRequestHeaders.Add(header.Key, header.Value);
                    }
                }
                var response = await client.GetAsync(url);
                response.EnsureSuccessStatusCode();
                return await response.Content.ReadAsStringAsync();
            } 
        }
现在我正在从我的操作中调用这个方法。
public async Task<ActionResult> Index()
        {
            try
            {
                RestWebRequest RestWebRequest = new RestWebRequest();
                Dictionary<string, string> headers = new Dictionary<string, string>();
                headers.Add("Authorization", "bearer _AxE9GWUO__8iIGS8stK1GrXuCXuz0xJ8Ba_nR1W2AhhOWy9r98e2_YquUmjFsAv1RcI94ROKEbiEjFVGmoiqmUU7qB5_Rjw1Z3FWMtzEc8BeM60WuIuF2fx_Y2FNTE_6XRhXce75MNf4-i0HbygnClzqDdrdG_B0hK6u2H7rtpBFV0BYZIUqFuJpkg4Aus85P8_Rd2KTCC5o6mHPiGxRl_yGFFTTL4_GvSuBQH39RoMqNj94A84KlE0hm99Yk-8jY6AKdxGRoEhtW_Ddow9FKWiViSuetcegzs_YWiPMN6kBFhY401ON_M_aH067ciIu6nZ7TiIkD5GHgndMvF-dYt3nAD95uLaqX6t8MS-WS2E80h7_AuaN5JZMOEOJCUi7z3zWMD2MoSwDtiB644XdmQ5DcJUXy_lli3KKaXgArJzKj85BWTAQ8xGXz3PyVo6W8swRaY5ojfnPUmUibm4A2lkRUvu7mHLGExgZ9rOsW_BbCDJq6LlYHM1BnAQ_W6LAE5P-DxMNZj7PNmEP1LKptr2RWwYt17JPRdN27OcSvZZdam6YMlBW00Dz2T2dgWqv7LvKpVhMpOtjOSdMhDzWEcf6yqr4ldVUszCQrPfjfBBtUdN_5nqcpiWlPx3JTkx438i08Ni8ph3gDQQvl3YL5psDcdwh0-QtNjEAGvBdQCwABvkbUhnIQQo_vwA68ITg07sEYgCl7Sql5IV7bD_x-yrlHyaVNtCn9C4zVr5ALIfj0YCuCyF_l1Z1MTRE7nb");
                var getCategories = await RestWebRequest.ExecuteGetHttp("http://localhost:53646/api/Job/GetAllCategories?isIncludeChild=true", headers);
            }
            catch (HttpRequestException ex)
            {
                return View();
            }
  return View();
  }

现在有人说,HttpClient已经被设计为可以重复使用多次调用.

如何在多个调用中使用同一个httpClient对象。

假设现在我先调用:

http://localhost:53646/api/Job/GetAllCategories?isIncludeChild=true

然后在同一控制器中,我必须使用不同的标头和不同的URL调用另一个API。

我应该创建HttpClient的全局对象并在所有API调用中使用同一个对象吗?


我认为是这样的。你有没有想到任何不能将它全局存储并重用于所有调用的具体原因? - Falgantil
1
看看我的博客文章(实际上是关于.NET Core中HttpClient最佳实践的):https://bytedev.medium.com/net-core-httpclient-best-practices-4c1b20e32c6 - bytedev
1
请查看以下文章:https://www.thecodebuzz.com/using-httpclient-best-practices-and-anti-patterns/ - NoWar
3个回答

14

如果您想在应用程序中使用同一个HttpClient,但要使用不同的凭据或尝试更改请求的默认标头(或传递给HttpClientHandler的任何内容),那么就会出现问题。在这种情况下,您需要一组特定于目的的HttpClients进行重复使用,因为仅使用一个将会有问题。

我建议根据您希望进行的请求“类型”创建一个HttpClient并重复使用它们。例如,每个凭证一个 - 也许如果您有几组默认标头,则每个标头一个。

这可能是一个在HttpClient属性之间进行平衡(它们不是线程安全的)并且如果需要变化则需要自己的实例的挑战:

- BaseAddress
- DefaultRequestHeaders
- MaxResponseContentBufferSize
- Timeout

以下是翻译的结果:

您可以传递给“VERB”方法(get、put、post等)的内容。例如,使用HttpClient.PostAsync Method (String, HttpContent),您可以为[HttpContent][3]指定标头(而不必将它们放入HttpClient DefaultHeaders中)。

HttpClient的所有异步方法都是线程安全的(PostAsync等)。


5
每次请求创建一个HttpRequestMessage怎么样?然后使用HttpClient的SendAsync方法。这样您可以在请求之间指定不同的凭据,仍然可以重用相同的HttpClient。 - Jón Trausti Arason

8

仅仅因为你能够做到并不意味着你应该这样做。

当你想要在一个紧密的循环中发出许多HTTP请求时,你不一定需要,但是你可以重用HttpClient。这可以节约实例化对象所需的微小时间。

每次请求都会实例化你的MVC控制器。因此,在同一时间实例化一个HttpClient并不会损害任何大量时间。请记住,你将使用它来发出一个HTTP请求,这将比实例化更耗时。

如果你坚持要重用一个实例,因为你已经对它进行了基准测试,并评估了实例化HttpClient是最大瓶颈,那么你可以看一下依赖注入,并将一个单一实例注入到每个需要它的控制器中。


需要考虑您的使用情况,这是一个很好的观点。如果您需要最大的性能或担心打开的连接数量,则共享HttpClients将会有益。在使用DI时,请注意不要为每个调用解析,否则您的DI开销可能会抵消任何好处。 - Murray Foxcroft
@Murray HttpClient 使用连接池。 - CodeCaster
澄清一下,每个HttpClient实例都使用自己的连接池。 - Murray Foxcroft
6
除了考虑应用程序的性能,还应该考虑其稳定性和可靠性。 https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/ 提供了一个很好的解释。 - Arve Systad
1
@Larry,你说得完全正确,我在写完这个答案后了解到了HttpClient的问题。我会尽快更新它。 - CodeCaster
显示剩余3条评论

5

.net core中,你可以使用 HttpClientFactory 来做同样的事情,像这样:

public interface IBuyService
{
    Task<Buy> GetBuyItems();
}

public class BuyService: IBuyService
{
    private readonly HttpClient _httpClient;

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

    public async Task<Buy> GetBuyItems()
    {
        var uri = "Uri";

        var responseString = await _httpClient.GetStringAsync(uri);

        var buy = JsonConvert.DeserializeObject<Buy>(responseString);
        return buy;
    }
}

ConfigureServices

services.AddHttpClient<IBuyService, BuyService>(client =>
{
     client.BaseAddress = new Uri(Configuration["BaseUrl"]);
});

文档和示例在此处此处


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接