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

畫卷琴夢
发布: 2025-09-17 08:04:02
原创
319人浏览过

使用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
    登录后复制

    PatentPal专利申请写作
    PatentPal专利申请写作

    AI软件来为专利申请自动生成内容

    PatentPal专利申请写作 13
    查看详情 PatentPal专利申请写作
    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策略,使得重试逻辑与业务代码分离,更加清晰和可维护。这显著提升了客户端的健壮性和对外部服务波动的容忍度。

以上就是C#的HttpClient类如何发送HTTP请求?的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

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