0

0

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

畫卷琴夢

畫卷琴夢

发布时间:2025-09-17 08:04:02

|

329人浏览过

|

来源于php中文网

原创

使用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会通过构造函数注入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

    甲骨文AI协同平台
    甲骨文AI协同平台

    专门用于甲骨文研究的革命性平台

    下载
    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策略,使得重试逻辑与业务代码分离,更加清晰和可维护。这显著提升了客户端的健壮性和对外部服务波动的容忍度。

相关专题

更多
登录token无效
登录token无效

登录token无效解决方法:1、检查token的有效期限,如果token已经过期,需要重新获取一个新的token;2、检查token的签名,如果签名不正确,需要重新获取一个新的token;3、检查密钥的正确性,如果密钥不正确,需要重新获取一个新的token;4、使用HTTPS协议传输token,建议使用HTTPS协议进行传输 ;5、使用双因素认证,双因素认证可以提高账户的安全性。

6099

2023.09.14

登录token无效怎么办
登录token无效怎么办

登录token无效的解决办法有检查Token是否过期、检查Token是否正确、检查Token是否被篡改、检查Token是否与用户匹配、清除缓存或Cookie、检查网络连接和服务器状态、重新登录或请求新的Token、联系技术支持或开发人员等。本专题为大家提供token相关的文章、下载、课程内容,供大家免费下载体验。

810

2023.09.14

token怎么获取
token怎么获取

获取token值的方法:1、小程序调用“wx.login()”获取 临时登录凭证code,并回传到开发者服务器;2、开发者服务器以code换取,用户唯一标识openid和会话密钥“session_key”。想了解更详细的内容,可以阅读本专题下面的文章。

1063

2023.12.21

token什么意思
token什么意思

token是一种用于表示用户权限、记录交易信息、支付虚拟货币的数字货币。可以用来在特定的网络上进行交易,用来购买或出售特定的虚拟货币,也可以用来支付特定的服务费用。想了解更多token什么意思的相关内容可以访问本专题下面的文章。

1265

2024.03.01

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

482

2023.08.10

数据库Delete用法
数据库Delete用法

数据库Delete用法:1、删除单条记录;2、删除多条记录;3、删除所有记录;4、删除特定条件的记录。更多关于数据库Delete的内容,大家可以访问下面的文章。

269

2023.11.13

drop和delete的区别
drop和delete的区别

drop和delete的区别:1、功能与用途;2、操作对象;3、可逆性;4、空间释放;5、执行速度与效率;6、与其他命令的交互;7、影响的持久性;8、语法和执行;9、触发器与约束;10、事务处理。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

210

2023.12.29

http500解决方法
http500解决方法

http500解决方法有检查服务器日志、检查代码错误、检查服务器配置、检查文件和目录权限、检查资源不足、更新软件版本、重启服务器或寻求专业帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

374

2023.11.09

Java JVM 原理与性能调优实战
Java JVM 原理与性能调优实战

本专题系统讲解 Java 虚拟机(JVM)的核心工作原理与性能调优方法,包括 JVM 内存结构、对象创建与回收流程、垃圾回收器(Serial、CMS、G1、ZGC)对比分析、常见内存泄漏与性能瓶颈排查,以及 JVM 参数调优与监控工具(jstat、jmap、jvisualvm)的实战使用。通过真实案例,帮助学习者掌握 Java 应用在生产环境中的性能分析与优化能力。

19

2026.01.20

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WEB前端教程【HTML5+CSS3+JS】
WEB前端教程【HTML5+CSS3+JS】

共101课时 | 8.4万人学习

JS进阶与BootStrap学习
JS进阶与BootStrap学习

共39课时 | 3.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号