.Net4.5及.Net Core2.1下的HttpClient使用详解
一、HTTP系列演进
| 方式 | 说明 | 
|---|---|
| HttpWebRequest | .NET早期版本,同步方式 | 
| WebClient | HttpWebRequest的封装简化版,同步方式 | 
| HttpClient | .NET4.5以后,异步方式 | 
| HttpClientFactory | .NET Core2.1 | 
二、HttpClient用法
HttpClient 提供的方法:
 GetAsync(String) 	//以异步操作将GET请求发送给指定的URI
 GetAsync(URI) 	//以异步操作将GET请求发送给指定的URI
 GetAsync(String, HttpCompletionOption) 	//以异步操作的HTTP完成选项发送GET请求到指定的URI
 GetAsync(String, CancellationToken) 	//以异步操作的取消标记发送GET请求到指定URI
 GetAsync(Uri, HttpCompletionOption) 	//以异步操作的HTTP完成选项发送GET请求到指定的URI
 GetAsync(Uri, HttpCompletionOption, CancellationToken) 	//以异步操作的HTTP完成选项和取消标记发送DELETE请求到指定的URI
 GetAsync(Uri, HttpCompletionOption, CancellationToken) 	//以异步操作的HTTP完成选项和取消标记发送DELETE请求到指定的URI
 GetByteArrayAsync(String) 	//将GET请求发送到指定URI并在异步操作中以字节数组的形式返回响应正文
 GetByteArrayAsync(Uri) 	//将GET请求发送到指定URI并在一异步操作中以字节数组形式返回响应正文
 GetHashCode 	//用作特定类型的哈希函数,继承自Object
 GetStreamAsync(String) 	//将GET请求发送到指定URI并在异步操作中以流的形式返回响应正文
 GetStreamAsync(Uri) 	//将GET请求发送到指定URI并在异步操作以流的形式返回响应正文
 GetStreamAsync(String) 	//将GET请求发送到指定URI并在异步操作中以字符串的形式返回响应正文
 GetStringAsync(Uri) 	//将GET请求发送到指定URI并在异步操作中以字符串形式返回响应正文
using(var httpClient = new HttpClient())
{
    //other codes
}
以上用法是不推荐的,HttpClient 这个对象有点特殊,虽然继承了 IDisposable 接口,但它是可以被共享的(或者说可以被复用),且线程安全。从项目经验来看,推荐在整个应用的生命周期内复用 HttpClient 实例,而不是每次RPC请求的时候就实例化一个,在高并发的情况下,会造成Socket资源的耗尽。
示例1:
public class Program
{
   private static readonly HttpClient _httpClient = new HttpClient();
   static void Main(string[] args)
   {
       HttpAsync();
       Console.WriteLine("Hello World!");
       Console.Read();
   }
   public static async void HttpAsync()
   {
       for (int i = 0; i < 10; i++)
       {
           var result = await _httpClient.GetAsync("http://www.baidu.com");
           Console.WriteLine($"{i}:{result.StatusCode}");
       }
   }
}
示例2:
public async Task<string> GetAccessTokenAsync()
{
	string uri = "你的URL";
	HttpClientHandler handler = new HttpClientHandler
	{
		//设置是否发送凭证信息,有的服务器需要验证身份,不是所有服务器需要
		UseDefaultCredentials = false
	};
	HttpClient httpClient = new HttpClient(handler);
	HttpResponseMessage response = await httpClient.GetAsync(uri);
	response.EnsureSuccessStatusCode();
	//回复结果直接读成字符串
	string resp = await response.Content.ReadAsStringAsync();
	JObject json = (JObject)JsonConvert.DeserializeObject(resp);
	string accessToken = json["access_token"].ToString();
	//采用流读数据
	//using (Stream streamResponse = await response.Content.ReadAsStreamAsync())
	//{
	//    StreamReader reader = new StreamReader(streamResponse);
	//    string responseFromServer = reader.ReadToEnd();
	//    JObject res = (JObject)JsonConvert.DeserializeObject(responseFromServer);
	//    accessToken = res["access_token"].ToString();
	//    reader.Close();
	//}
	//获得许可证凭证
	PostMailAsync(accessToken);
	//关闭响应
	return "success";
}
优化:帮HttpClient预热
我们采用一种预热方式,在正式发post请求之前,先发一个head请求:
_httpClient.SendAsync(new HttpRequestMessage {
	Method = new HttpMethod("HEAD"),
	RequestUri = new Uri(BASE_ADDRESS + "/")
})
.Result.EnsureSuccessStatusCode();
经测试,通过这种热身方法,可以将第一次请求的耗时由2s左右降到1s以内(测试结果是700多ms)。
存在问题
复用 HttpClient 后,依然存在一些问题:
- 因为是复用的 HttpClient ,那么一些公共的设置就没办法灵活的调整了,如请求头的自定义。
 - 因为 HttpClient 请求每个 url 时,会缓存该 url 对应的主机 ip ,从而会导致 DNS 更新失效( TTL 失效了)
 
那么有没有办法解决HttpClient的这些个问题?直到 HttpClientFactory 的出现,这些坑 “完美” 规避掉了。
三、HttpClientFactory
- HttpClientFacotry 很高效,可以最大程度上节省系统 socket 。(“JUST USE IT AND FXXK SHUT UP”)
 - Factory,顾名思义 HttpClientFactory 就是 HttpClient 的工厂,内部已经帮我们处理好了对 HttpClient 的管理,不需要我们人工进行对象释放,同时,支持自定义请求头,支持DNS更新等等等。
 - 从微软源码分析,HttpClient 继承自 HttpMessageInvoker ,而 HttpMessageInvoker 实质就是 HttpClientHandler 。
 - HttpClientFactory 创建的 HttpClient ,也即是 HttpClientHandler ,这些个 HttpClient 被放到了“池子”中,工厂每次在 create 的时候会自动判断是新建还是复用。(默认生命周期为 2 min)
 
示例1
- 在 Startup.cs 中进行注册
 
public class Startup
{
	public Startup(IConfiguration configuration)
	{
		Configuration = configuration;
	}
	public IConfiguration Configuration { get; }
	public void ConfigureServices(IServiceCollection services)
	{
		//other codes
		services.AddHttpClient("client_1", config => //这里指定的 name=client_1 ,可以方便我们后期复用该实例
		{
			config.BaseAddress = new Uri("http://client_1.com");
			config.DefaultRequestHeaders.Add("header_1", "header_1");
		});
		services.AddHttpClient("client_2", config =>
		{
			config.BaseAddress = new Uri("http://client_2.com");
			config.DefaultRequestHeaders.Add("header_2", "header_2");
		});
		services.AddHttpClient();
		//other codes
		services.AddMvc().AddFluentValidation();
	}
}
- 使用,这里直接以 controller 为例,其他地方自行 DI
 
public class TestController : ControllerBase
{
	private readonly IHttpClientFactory _httpClient;
	public TestController(IHttpClientFactory httpClient)
	{
		_httpClient = httpClient;
	}
	public async Task<ActionResult> Test()
	{
		var client = _httpClient.CreateClient("client_1"); //复用在 Startup 中定义的 client_1 的 httpclient
		var result = await client.GetStringAsync("/page1.html");
		var client2 = _httpClient.CreateClient(); //新建一个 HttpClient
		var result2 = await client.GetStringAsync("http://www.site.com/XXX.html");
		return null;
	}
}
示例2:使用自定义类执行 HttpClientFactory 请求
- 自定义 HttpClientFactory 请求类
 
public class SampleClient
{
    public HttpClient Client { get; private set; }
    public SampleClient(HttpClient httpClient)
    {
        httpClient.BaseAddress = new Uri("https://api.SampleClient.com/");
        httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
        httpClient.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
        Client = httpClient;
    }
}
- 在 Startup.cs 中 ConfigureService 方法中注册 SampleClient
 
services.AddHttpClient<SampleClient>();
- 调用:
 
public class ValuesController : Controller
{
	private readonly SampleClient  _sampleClient;
	public ValuesController(SampleClient  sampleClient)
	{
		_sampleClient = sampleClient;
	}
	[HttpGet]
	public async Task<ActionResult> Get()
	{
		string result = await  _sampleClient.client.GetStringAsync("/");
		return Ok(result);
	}
}
示例3:完全封装 HttpClient 可以使用下面方法
- 自定义 HttpClientFactory 请求类
 
public interface ISampleClient
{
    Task<string> GetData();
}
public class SampleClient : ISampleClient
{
    private readonly HttpClient _client;
    public SampleClient(HttpClient httpClient)
    {
        httpClient.BaseAddress = new Uri("https://api.SampleClient.com/");
        httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
        httpClient.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
        _client = httpClient;
    }
    public async Task<string> GetData()
    {
        return await _client.GetStringAsync("/");
    }
}
- 在Startup.cs中ConfigureService方法中注册SampleClient
 
services.AddHttpClient<ISampleClient, SampleClient>();
- 调用:
 
public class ValuesController : Controller
{
    private readonly ISampleClient  _sampleClient;
    public ValuesController(ISampleClient  sampleClient)
    {
        _sampleClient = sampleClient;
    }
    [HttpGet]
    public async Task<ActionResult> Get()
    {
        string result = await _sampleClient.GetData();
        return Ok(result);
    }
}
微软官网:在 ASP.NET Core 中使用 IHttpClientFactory 发出 HTTP 请求
.Net4.5及.Net Core2.1下的HttpClient使用详解的更多相关文章
- Mac下安装HBase及详解
		
Mac下安装HBase及详解 1. 千篇一律的HBase简介 HBase是Hadoop的数据库, 而Hive数据库的管理工具, HBase具有分布式, 可扩展及面向列存储的特点(基于谷歌BigTabl ...
 - ava下static关键字用法详解
		
Java下static关键字用法详解 本文章介绍了java下static关键字的用法,大部分内容摘自原作者,在此学习并分享给大家. Static关键字可以修饰什么? 从以下测试可以看出, static ...
 - Linux下tomcat的安装详解
		
Linux下tomcat的安装详解 来源: ChinaUnix博客 日期: 2007.01.21 22:59 (共有0条评论) 我要评论 一,安装前的准备:1,Linux版本:我的是企业版.(至于红帽 ...
 - Linux下的文件目录结构详解
		
Linux下的文件目录结构详解 / Linux文件系统的上层根目录 /bin 存放用户可执行的程序 /boot 操作系统启动时所需要的文件 /dev 接口设备文件目录,例如:had表示硬盘 /etc ...
 - Linux下find命令用法详解
		
Linux下find命令用法详解 学神VIP烟火 学神IT教育:XueGod-IT 最负责任的线上直播教育平台 本文作者为VIP学员 烟火 第一部分:根据文件名查找 1.在当前目录 ...
 - Spring Boot 2.x 快速入门(下)HelloWorld示例详解
		
上篇 Spring Boot 2.x 快速入门(上)HelloWorld示例 进行了Sprint Boot的快速入门,以实际的示例代码来练手,总比光看书要强很多嘛,最好的就是边看.边写.边记.边展示. ...
 - Django--filter()-字段查找(双下划线的使用详解)
		
Django--filter()-字段查找(双下划线的使用详解) 在了解django中的字段查找的同时,让我们先熟悉一下比较符: 大于--gt-(greater than) 小于--lt-(less ...
 - (转)windows 下安装配置 Nginx 详解
		
windows 下安装配置 Nginx 详解 本文转自https://blog.csdn.net/kingscoming/article/details/79042874 nginx功能之一可以启动一 ...
 - linux下sort命令使用详解---linux将文本文件内容加以排序命令
		
转载自:http://www.cnblogs.com/hitwtx/archive/2011/12/03/2274592.html linux下sort命令使用详解---linux将文本文件内容加以排 ...
 - linux下getsockopt和setsockopt详解及测试
		
linux下getsockopt和setsockopt详解及测试 NAME 名字 getsockopt, setsockopt - get and set options on sockets 获取或 ...
 
随机推荐
- CSS操作——display属性
			
display可以指定元素的显示模式,它可以把行内元素修改成块状元素,也可以把别的模式的元素改成行内元素.diisplay常用的值有四个. 语法: /* display: block; // 声明当前 ...
 - RCTF 2024 WEB wp
			
RCTF 2024 WEB wp 前言 赛后复现,proxy发现自己真是个呆b... what_is_love 首先拿key1,sql语句处有注入,可以盲注拿key1的值 import request ...
 - 莫烦tensorflow学习记录 (7)循环神经网络 RNN  &  LSTM
			
莫凡大佬的原文章https://mofanpy.com/tutorials/machine-learning/tensorflow/intro-RNN/ RNN 的用途 可以读取数据中的顺序,获取顺序 ...
 - 16位简单ASM题的记录——[HGAME 2022 week1]easyasm
			
第一次遇见16位,和纯看汇编的题目,记录一下 DIE 16位,IDA用32位或者64位都可以打开 IDA 主要汇编部分 seg003:0000 ; =============== S U B R O ...
 - react减少组件渲染
			
当this.setState()修改了state中的数据后,当前组件将重新渲染,同时也会重新渲染子组件,但只会渲染当前组件子树(当前组件以其所有子组件) shouldComponentUpdate 当 ...
 - 一文了解 - -> SpringMVC
			
一.SpringMVC概述 Spring MVC 是由Spring官方提供的基于MVC设计理念的web框架. SpringMVC是基于Servlet封装的用于实现MVC控制的框架,实现前端和服务端的交 ...
 - apollo配置json
			
#json串原文[{"username":"李小刚","sex":"男"},{"username": ...
 - 架构师必知的11种API性能优化方法
			
前言 接口性能优化是后端开发人员经常碰到的一道面试题,因为它是一个跟开发语言无关的公共问题. 这个问题既可以很简单,也可以相当复杂. 有时候,只需要添加一个索引就能解决. 有时候,代码需要进行重构. ...
 - MoneyPrinterPlus:AI自动短视频生成工具-微软云配置详解
			
MoneyPrinterPlus可以使用大模型自动生成短视频,我们可以借助Azure提供的语音服务来实现语音合成和语音识别的功能. Azure的语音服务应该是我用过的效果最好的服务了,微软还得是微软. ...
 - 高通mm-camera平台 Camera移植
			
高通Cam-X平台 Camera移植 注:此文档以在高通8916平台移植OV5648为例,给大家讲解Android SOC的底层Camera. Reference: https://blog.csdn ...