C#的HttpClient类如何发送HTTP请求?

使用httpclient时需复用实例或使用httpclientfactory管理生命周期。1.避免为每个请求创建新httpclient实例,以防止端口耗尽和dns解析浪费;2.推荐将httpclient声明为静态或使用httpclientfactory进行依赖注入,以实现连接复用并解决dns缓存问题;3.httpclientfactory通过管理httpmessagehandler的生命周期,既提升性能又确保dns更新及时生效。

C#的HttpClient类如何发送HTTP请求?

HttpClient

在C#中是发送HTTP请求的核心工具,它提供了一套简洁而强大的API来处理各种Web交互,无论是GET、POST还是其他请求,都能轻松应对。

解决方案

使用C#的

HttpClient

发送HTTP请求,最基本的步骤通常涉及创建

HttpClient

实例,构造

HttpRequestMessage

(或直接使用其便捷方法),然后发送请求并处理响应。一个常见的误区是为每个请求都创建一个新的

HttpClient

实例,这其实是效率低下的做法,甚至可能导致端口耗尽。更推荐的做法是复用同一个

HttpClient

实例,或者使用

HttpClientFactory

来管理它们的生命周期。

我们来看一个发送GET请求的例子。假设你需要从某个API获取数据:

using System; using System.Net.Http; using System.Threading.Tasks;  public class HttpRequestSender {     // 推荐的做法:复用HttpClient实例     // 实际项目中,更推荐使用HttpClientFactory     private static readonly HttpClient _httpClient = new HttpClient();      public async Task GetExampleAsync(string url)     {         try         {             // 发送GET请求并等待响应             HttpResponseMessage response = await _httpClient.GetAsync(url);              // 确保请求成功(状态码2xx)             response.EnsureSuccessStatusCode();              // 读取响应内容             string responseBody = await response.Content.ReadAsStringAsync();             Console.WriteLine($"GET 请求成功,响应内容:n{responseBody}");         }         catch (HttpRequestException e)         {             Console.WriteLine($"GET 请求出错: {e.Message}");             // 这里可以根据e.StatusCode进行更细致的错误处理         }         catch (TaskCanceledException e) when (e.InnerException is TimeoutException)         {             Console.WriteLine($"GET 请求超时: {e.Message}");         }         catch (Exception e)         {             Console.WriteLine($"发生未知错误: {e.Message}");         }     }      public async Task PostExampleAsync(string url, string jsonContent)     {         try         {             // 准备POST请求的内容             StringContent content = new StringContent(jsonContent, System.Text.Encoding.UTF8, "application/json");              // 发送POST请求             HttpResponseMessage response = await _httpClient.PostAsync(url, content);              response.EnsureSuccessStatusCode(); // 检查状态码              string responseBody = await response.Content.ReadAsStringAsync();             Console.WriteLine($"POST 请求成功,响应内容:n{responseBody}");         }         catch (HttpRequestException e)         {             Console.WriteLine($"POST 请求出错: {e.Message}");         }         catch (Exception e)         {             Console.WriteLine($"发生未知错误: {e.Message}");         }     } }  // 调用示例 /* public class Program {     public static async Task Main(string[] args)     {         HttpRequestSender sender = new HttpRequestSender();         // 假设这是一个真实存在的API地址         await sender.GetExampleAsync("https://jsonplaceholder.typicode.com/todos/1");          string postData = "{"title":"foo","body":"bar","userId":1}";         await sender.PostExampleAsync("https://jsonplaceholder.typicode.com/posts", postData);     } } */

这段代码展示了

HttpClient

的基本用法。你会发现,它高度依赖异步操作(

async

/

await

),这是现代C#进行I/O操作的推荐方式,因为它能有效避免阻塞线程,提升应用程序的响应能力和并发性能。

如何处理HttpClient的生命周期和连接池问题?

这是一个老生常谈但又极其关键的问题。许多开发者,包括我自己在初学时,都曾陷入为每个请求创建新

HttpClient

的陷阱。表面上看,这似乎很合理,用完即丢,避免资源泄露。但实际上,

HttpClient

内部管理着连接池,每次新建实例,都会创建一个新的底层TCP连接,这不仅开销大,还可能导致端口耗尽(

SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted

)。更糟糕的是,新的连接意味着每次请求都需要重新进行DNS解析,这在服务地址不变的情况下完全是浪费。

正确的姿势是复用

HttpClient

实例。一个

HttpClient

实例可以安全地被多个线程并发使用。它被设计成一个长期存活的对象。在桌面应用或控制台应用中,你完全可以将其声明为静态成员,或者作为单例注入。

然而,长期复用一个

HttpClient

也并非没有缺点,最典型的就是DNS缓存问题

HttpClient

实例一旦创建,它内部的DNS解析结果就会被缓存。如果你的服务部署在负载均衡器后面,IP地址可能会动态变化,或者在服务迁移后IP地址更新,但

HttpClient

可能仍然尝试连接旧的IP地址,导致请求失败。

为了解决这个DNS缓存和生命周期管理的平衡问题,.NET Core 2.1及更高版本引入了

IHttpClientFactory

。这是处理

HttpClient

生命周期的最佳实践。

IHttpClientFactory

不是直接返回

HttpClient

实例,而是返回一个“逻辑”

HttpClient

实例,它背后会从池中借用或创建

HttpMessageHandler

,这个

HttpMessageHandler

才是实际管理连接和DNS缓存的部分。

IHttpClientFactory

会定期回收旧的

HttpMessageHandler

,从而解决了DNS缓存过时的问题,同时又保持了连接复用带来的性能优势。

在ASP.NET Core应用中,你通常会在

Startup.cs

ConfigureServices

方法中注册它:

// 在Startup.cs的ConfigureServices方法中 services.AddHttpClient(); // 注册默认的HttpClient // 或者注册一个具名客户端 services.AddHttpClient("myApi", client => {     client.BaseAddress = new Uri("https://api.example.com/");     client.DefaultRequestHeaders.Add("Accept", "application/json"); }); // 还可以注册一个类型化客户端 services.AddHttpClient<MyApiService>(); // MyApiService会通过构造函数注入HttpClient

然后,在你的服务类中通过构造函数注入

HttpClient

IHttpClientFactory

。这样,框架会为你处理

HttpClient

的创建、复用和销毁,让你能够专注于业务逻辑。

发送不同类型的HTTP请求(如PUT、DELETE)和携带请求头、认证信息有哪些技巧?

HttpClient

不仅支持GET和POST,对于PUT、DELETE等HTTP动词也提供了类似的便捷方法,或者你可以通过

HttpRequestMessage

来构建更复杂的请求。

发送PUT和DELETE请求:

using System.Net.Http; using System.Text; using System.Threading.Tasks;  public class AdvancedHttpRequestSender {     private static readonly HttpClient _httpClient = new HttpClient();      public async Task PutExampleAsync(string url, string jsonContent)     {         StringContent content = new StringContent(jsonContent, Encoding.UTF8, "application/json");         HttpResponseMessage response = await _httpClient.PutAsync(url, content);         response.EnsureSuccessStatusCode();         string responseBody = await response.Content.ReadAsStringAsync();         Console.WriteLine($"PUT 请求成功,响应内容:n{responseBody}");     }      public async Task DeleteExampleAsync(string url)     {         HttpResponseMessage response = await _httpClient.DeleteAsync(url);         response.EnsureSuccessStatusCode();         Console.WriteLine($"DELETE 请求成功,状态码: {response.StatusCode}");     } }

携带请求头:

有几种方式可以添加请求头:

  1. 全局请求头 (

    DefaultRequestHeaders

    ): 如果你的所有请求都需要相同的头,比如

    User-Agent

    Accept

    ,可以设置在

    HttpClient

    实例的

    DefaultRequestHeaders

    上。

    _httpClient.DefaultRequestHeaders.Add("User-Agent", "MyC#App/1.0"); _httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
  2. 针对单个请求的请求头 (

    HttpRequestMessage

    ): 对于特定请求才需要的头,或者需要覆盖全局设置的头,可以使用

    HttpRequestMessage

    C#的HttpClient类如何发送HTTP请求?

    一帧秒创

    基于秒创AIGC引擎的AI内容生成平台,图文转视频,无需剪辑,一键成片,零门槛创作视频。

    C#的HttpClient类如何发送HTTP请求?41

    查看详情 C#的HttpClient类如何发送HTTP请求?

    HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://api.example.com/data"); request.Headers.Add("X-Custom-Header", "MyValue"); HttpResponseMessage response = await _httpClient.SendAsync(request);

认证信息:

认证通常通过请求头来传递,最常见的是Bearer Token(OAuth 2.0)和Basic Authentication。

  • Bearer Token: 这是现代API中最常见的认证方式。

    _httpClient.DefaultRequestHeaders.Authorization =      new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", "your_access_token_here");  // 或者针对单个请求 HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://api.example.com/secure_data"); request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", "your_access_token_here"); HttpResponseMessage response = await _httpClient.SendAsync(request);
  • Basic Authentication: 虽然不如Bearer Token安全,但在某些场景下仍在使用。它需要将用户名和密码用冒号连接后进行Base64编码

    string authString = Convert.ToBase64String(Encoding.ASCII.GetBytes("username:password")); _httpClient.DefaultRequestHeaders.Authorization =      new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authString);

处理HttpClient请求中的异常、超时和重试机制?

网络请求总是充满不确定性,异常处理、超时设置和重试机制是构建健壮客户端的关键。

异常处理:

HttpClient

的请求可能会抛出几种类型的异常:

  • HttpRequestException

    这是最常见的,当HTTP请求本身失败时(例如,DNS解析失败、连接中断、服务器返回非2xx状态码时调用

    EnsureSuccessStatusCode()

    ),就会抛出此异常。

    HttpRequestException

    有一个

    StatusCode

    属性,可以让你检查具体的HTTP状态码,从而进行更细致的错误处理(例如,401未授权、404未找到、500服务器内部错误等)。

    try {     HttpResponseMessage response = await _httpClient.GetAsync(url);     response.EnsureSuccessStatusCode(); // 如果状态码不是2xx,这里会抛出HttpRequestException     // ... } catch (HttpRequestException ex) {     Console.WriteLine($"请求失败: {ex.Message}");     if (ex.StatusCode.HasValue)     {         Console.WriteLine($"HTTP状态码: {ex.StatusCode.Value}");     } }
  • TaskCanceledException

    当请求被取消或超时时,会抛出此异常。特别是当请求超时时,它的

    InnerException

    通常是

    TimeoutException

    try {     // ... } catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException) {     Console.WriteLine($"请求超时: {ex.Message}"); } catch (TaskCanceledException ex) {     Console.WriteLine($"请求被取消: {ex.Message}"); }

超时设置:

HttpClient

有一个

Timeout

属性,用于设置请求的超时时间。如果在这个时间内没有收到响应,请求就会被取消并抛出

TaskCanceledException

(内部带有

TimeoutException

)。

_httpClient.Timeout = TimeSpan.FromSeconds(10); // 设置10秒超时

这个超时是针对整个请求过程的,包括连接、发送请求和接收响应。对于某些需要长时间处理的请求,你可能需要适当延长这个时间。

重试机制:

对于瞬时性错误(例如网络抖动、服务器短暂过载、429 Too Many Requests),简单的重试通常能解决问题。手动实现重试逻辑会比较繁琐,因为它需要处理延迟、指数退避(每次重试间隔时间逐渐增加)以及最大重试次数等。

在.NET生态系统中,Polly是一个非常流行的弹性策略库,它提供了优雅的方式来实现重试、断路器、超时等多种弹性策略。使用Polly,你可以像这样定义一个重试策略:

// 这是一个概念性的示例,Polly的实际用法会更详细 // using Polly; // using Polly.Extensions.Http;  // 定义一个重试策略:重试3次,每次重试间隔时间递增 // var retryPolicy = HttpPolicyExtensions //     .HandleTransientHttpError() // 处理瞬时HTTP错误(5xx, 408, DNS等) //     .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound) // 也可以处理特定状态码 //     .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));  // 然后在发送请求时应用这个策略 // HttpResponseMessage response = await retryPolicy.ExecuteAsync(() => _httpClient.GetAsync(url));

将Polly与

IHttpClientFactory

结合使用是最佳实践,

IHttpClientFactory

允许你在注册

HttpClient

时直接添加Polly策略,使得重试逻辑与业务代码分离,更加清晰和可维护。这显著提升了客户端的健壮性和对外部服务波动的容忍度。

word js json 编码 app access 端口 工具 ai dns c# dns解析失败 asic 构造函数 Token 线程 delete 并发 对象 异步 http 负载均衡

上一篇
下一篇