.NET Core 中正确使用 HttpClient 的姿势

Posted lihan829

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.NET Core 中正确使用 HttpClient 的姿势相关的知识,希望对你有一定的参考价值。

来源: https://mp.weixin.qq.com/s?__biz=MzAxMTA2MDYwNg==&mid=2650110453&idx=1&sn=63a84a1b86594c98eec0cc4149604fd2&scene=21#wechat_redirect

用途: 备忘

为了更方便在服务端调用 HTTP 请求,微软在 .NET Framework 4.x 的时候引入了 HttpClient。但 HttpClient 有很多严重问题,一直饱受诟病,比如 InfoQ 的这篇文章 t.cn/Evzy80y,吐槽了 HttpClient 不能立即关闭连接、性能消耗严重等的问题。

.NET Core 2.1 开始引入的 HttpClientFactory 解决了 HttpClient 的所有痛点。有了 HttpClientFactory,我们不需要关心如何创建 HttpClient,又如何释放它。通过它可以创建具有特定业务的 HttpClient,而且可以很友好的和 DI 容器结合使用,更为灵活。下面以 ASP.NET Core 为例介绍 HttpClient 新的三种正确使用方式。

一、直接使用方式

不管是哪种方式,都要先注册服务,对于 ASP.NET Core 应用则是在 Startup.cs 文件的 ConfigureServices添加如果代码:

services.AddHttpClient();

 

然后在 Controller 中通过构造注入的试获得 IHttpClientFactory,然后通过它来创建 HttpClient 对象。示例代码:

public class ValuesController : BaseController



   private readonly IHttpClientFactory _httpClientFactory;

   public ValuesController(IHttpClientFactory httpClientFactory)

   

       _httpClientFactory = httpClientFactory;

   

   [HttpGet]

   public async Task<ActionResult> Get()

   

       var client = _httpClientFactory.CreateClient();

       client.BaseAddress = new Uri("http://api.github.com");

       string result = await client.GetStringAsync("/");

       return Ok(result);

   

 

这种使用方式适合一次性的 HTTP 请求调用,弊端是如果多次都要请求 github 的接口,那就得写很多重复代码配置 HttpClient。

二、命名式使用方式

命名式的使用方式可以解决第一种方式的问题,对于特定域名的 HTTP 调用,可以只做一次配置,然后在多个地方通过名称拿到相同的 Client 来使用。

首先在 Startup.cs 注册服务的地方添加带有特定名称的 HttpClient 服务,并添加需要的配置,如下示例:

services.AddHttpClient();

services.AddHttpClient("github", c =>



   c.BaseAddress = new Uri("https://api.github.com/");

   c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");

   c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");

);

 

注意这里添加了两次 AddHttpClient,一次是没有命名的,一次是有命名的。在 Controller 中的使用方式是:

public class ValuesController : BaseController



   private readonly IHttpClientFactory _httpClientFactory;

   public ValuesController(IHttpClientFactory httpClientFactory)

   

       _httpClientFactory = httpClientFactory;

   

   [HttpGet]

   public async Task<ActionResult> Get()

   

       var client = _httpClientFactory.CreateClient("github");

       string result = await client.GetStringAsync("/");

       return Ok(result);

   

 

但如果使用很频繁,这种获取 Client 的方式还是有一点麻烦。下面介绍第三种方式,可以在使用的时候方便一点,但配置要麻烦些。

三、类型化使用方式

类型化的使用方式可以预先把配置放到自定义的 HttpClient 中,然后在需要的地方都过依赖注入直接拿到 HttpClient 实例。

我们来看具体的例子,首先自定义一个 HttClient 类,比如访问 github 接口的类可以这样定义:

public class GitHubClient



   public HttpClient Client  get; private set; 

   public GitHubClient(HttpClient httpClient)

   

       httpClient.BaseAddress = new Uri("https://api.github.com/");

       httpClient.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");

       httpClient.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");

       Client = httpClient;

   

   public async Task<string> GetData()

   

       return await _client.GetStringAsync("/");

   

 

这里的 GetData 就是额外扩展的共用方法。使用前需要为这个类注册服务:

services.AddHttpClient<GitHubClient>();

 

然后在需要的地方(比如 Controller)中这样使用:

public class ValuesController : BaseController



   private readonly GitHubClient _gitHubClient;;

   public ValuesController(GitHubClient gitHubClient)

   

       _gitHubClient = gitHubClient;

   

   [HttpGet]

   public async Task<ActionResult> Get()

   

       string result = await _gitHubClient.GetData();

       return Ok(result);

   

 

类型化的方式和命名式的方式相比,虽然在配置的时候会麻烦一点,但它有两个好处:一是不用通过名称字符串来获得 Client,而是直接通过依赖注入获得具体的 Client;二是可以在自定义的 HttpClient 类中扩展共用的方法。

 

 配合如下代码食用风味更佳哦, 需要添加Microsoft.Extensions.Http.Polly包:
services.AddHttpClient("GitHub", client =>

    client.BaseAddress = new Uri("https://api.github.com/");
    client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
)
.AddTransientHttpErrorPolicy(builder => builder.WaitAndRetryAsync(new[]

    TimeSpan.FromSeconds(1),
    TimeSpan.FromSeconds(5),
    TimeSpan.FromSeconds(10)
));

这里用了一个新的AddTransientHttpErrorPolicy方法,它可以很方便地配置一个策略来处理下面这些典型的HTTP调用错误:

  • 网络错误(HttpRequestException 异常)

  • HTTP状态码 5XX(服务器错误)

  • HTTP状态码 408(请求超时)

AddTransientHttpErrorPolicy方法添加了一个策略,这个策略默认预配置了上面HTTP错误的过滤器。在builder => builder子句中,你可以定义策略如何处理这些错误,还可以配置Polly提供的其它策略,比如重试(如上例所示)、断路或回退等。

在AddTransientHttpErrorPolicy中处理网络错误、HTTP 5XX和HTTP 408是一种便捷的方式,但这不是必需的。如果此方法内置的错误过滤器不适合您的需要(你需要仔细考虑一下),您可以扩展它,或者构建一个完全定制的Polly策略。

以上是关于.NET Core 中正确使用 HttpClient 的姿势的主要内容,如果未能解决你的问题,请参考以下文章

.NET Core 中正确使用 HttpClient 的姿势

如何正确解析要在 ASP.NET Core 3.1 的 ConfigureServices() 中使用的服务?

如何正确地将 OData 与 ASP.net Core 集成

.net core razor 页面中的多个视图组件未正确绑定

如何使用 ASP.NET Core DataProtection 正确配置 OpenIddict

Asp.Net Core 正确配置身份认证中间件