C#.NET HttpClient 使用教程

简介

HttpClient.NET 中用于发送 HTTP 请求和接收 HTTP 响应的现代化 API,它取代了过时的 WebClientHttpWebRequest 类。

HttpClient.NET Framework 4.5 + 和 .NET Core/.NET 5+ 中提供的、基于消息处理管道(message handler pipeline)的现代 HTTP 客户端库。

相比早期的 HttpWebRequest,它更易用、支持异步、可扩展性强,并且在 .NET Core 中底层使用 SocketsHttpHandler,性能与可配置性都有显著提升。

底层架构

  • HttpClient

    • 封装请求创建、发送及响应处理逻辑,提供 GetAsync、PostAsync、SendAsync 等方法。
  • HttpMessageHandler 管道

    • 默认使用 SocketsHttpHandler(.NET Core/.NET 5+).NET Framework 下为 HttpClientHandler

    • 可以通过继承 DelegatingHandler 串联多个自定义中间件(例如日志、重试、认证)。

  • 连接池与长连接

    • SocketsHttpHandler 内置连接池和 HTTP/2 支持,同一目标主机复用 TCP 连接,减少握手与重建成本。

HttpClient 核心特性

特性说明优势
异步支持所有方法都有异步版本避免阻塞线程,提高并发能力
连接池自动管理 HTTP 连接减少 TCP 连接开销
超时控制可配置请求超时时间防止长时间阻塞
内容处理内置多种内容处理器简化 JSON/XML 处理
取消支持支持 CancellationToken优雅终止长时间请求
扩展性可通过 DelegatingHandler 扩展实现日志、重试等中间件

常用方法

使用单例
public static class HttpClientSingleton
{
    public static readonly HttpClient Instance = new HttpClient();
}

// 使用
await HttpClientSingleton.Instance.GetAsync(url);
  • 全局唯一,避免重复创建 handler,复用连接池。

  • 需注意:设置到期(Timeout)、默认头(DefaultRequestHeaders)等全局属性变更会影响所有调用。

使用 HttpClientFactory 工厂
services.AddHttpClient("GitHub", client =>
{
    client.BaseAddress = new Uri("https://api.github.com/");
    client.DefaultRequestHeaders.Add("User-Agent", "MyApp");
})
.AddTransientHttpErrorPolicy(policy =>
    policy.WaitAndRetryAsync(3, _ => TimeSpan.FromSeconds(2)));

// 使用
var client = _httpClientFactory.CreateClient("GitHub");
  • 框架管理生命周期、稳定重用 SocketsHttpHandler

  • 支持命名客户端、类型化客户端、配置管道、添加 Polly 弹性策略。

类型化客户端
public class GitHubService {
    private readonly HttpClient _client;
    public GitHubService(HttpClient client) => _client = client;
    // 封装API方法
}
// 注册
services.AddHttpClient<GitHubService>(client => { /* 配置 */ });
GET 请求
var client = HttpClientSingleton.Instance;
var response = await client.GetAsync("https://api.example.com/items/1");
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
POST 请求
var content = new StringContent(
    JsonSerializer.Serialize(new { Name = "测试" }),
    Encoding.UTF8, "application/json");
var postResp = await client.PostAsync("https://api.example.com/items", content);
postResp.EnsureSuccessStatusCode();
SendAsync 自定义请求
var request = new HttpRequestMessage(HttpMethod.Put, $"items/{id}")
{
    Content = new StringContent(json, Encoding.UTF8, "application/json")
};
request.Headers.Add("X-Custom", "Value");

using var resp = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
resp.EnsureSuccessStatusCode();
流式上传/下载
// 下载
using var resp = await client.GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead);
using var stream = await resp.Content.ReadAsStreamAsync();
// 将 stream 写入文件 ...

// 上传
using var fs = File.OpenRead(localFilePath);
var streamContent = new StreamContent(fs);
var uploadResp = await client.PostAsync(uploadUrl, streamContent);
DelegatingHandler 管道
public class LoggingHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken ct)
    {
        Console.WriteLine($"Request: {request}");
        var resp = await base.SendAsync(request, ct);
        Console.WriteLine($"Response: {resp}");
        return resp;
    }
}

// 注册(HttpClientFactory)
services.AddHttpClient("WithLogging")
    .AddHttpMessageHandler<LoggingHandler>();
Polly 弹性策略
services.AddHttpClient("GitHub")
    .AddTransientHttpErrorPolicy(p =>
        p.WaitAndRetryAsync(3, retry => TimeSpan.FromSeconds(1)));
  • AddTransientHttpErrorPolicy 捕获 5xx、408、HttpRequestExceptiontransient 错误;

  • 可组合断路器、超时、隔离策略。

认证与配置
  • Bearer Token
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", accessToken);
  • Client Certificates
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(new X509Certificate2("cert.pfx", "pwd"));
var client = new HttpClient(handler);
  • 代理
handler.Proxy = new WebProxy("http://127.0.0.1:8888");
handler.UseProxy = true;
设置超时和请求头
httpClient.Timeout = TimeSpan.FromSeconds(30); // 设置全局超时
httpClient.DefaultRequestHeaders.Add("User-Agent", "MyApp/1.0");
httpClient.DefaultRequestHeaders.Authorization = 
    new AuthenticationHeaderValue("Bearer", "your-token");
处理不同类型的响应
// 获取 JSON 响应
var jsonResponse = await httpClient.GetStringAsync("https://api.example.com/json");

// 获取二进制响应(如文件下载)
var bytes = await httpClient.GetByteArrayAsync("https://example.com/file.pdf");

// 获取流响应(大文件下载,避免内存溢出)
await using var stream = await httpClient.GetStreamAsync("https://example.com/largefile.zip");

await using var fileStream = File.Create("largefile.zip");
await stream.CopyToAsync(fileStream);
发送带参数的请求
// GET 请求带查询参数
var queryParams = new Dictionary<string, string>
{
    { "page", "1" },
    { "size", "20" }
};
var uri = new UriBuilder("https://api.example.com/data")
{
    Query = new FormUrlEncodedContent(queryParams).ReadAsStringAsync().Result
};
var response = await httpClient.GetAsync(uri.Uri);

// POST 请求带表单数据
var formContent = new FormUrlEncodedContent(new[]
{
    new KeyValuePair<string, string>("username", "test"),
    new KeyValuePair<string, string>("password", "pass")
});
await httpClient.PostAsync("https://example.com/login", formContent);
处理 HTTP 错误
try
{
    var response = await httpClient.GetAsync("https://api.example.com/resource");
    response.EnsureSuccessStatusCode(); // 非 200 状态码会抛出异常
}
catch (HttpRequestException ex)
{
    Console.WriteLine($"HTTP Error: {ex.StatusCode}");
    Console.WriteLine($"Message: {ex.Message}");
}
处理 gzip/deflate 压缩响应
var httpClientHandler = new HttpClientHandler
{
    AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};
var httpClient = new HttpClient(httpClientHandler);
取消操作
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); // 10秒超时
try {
    var response = await client.GetAsync("https://slow-api.com", cts.Token);
} catch (TaskCanceledException) {
    // 处理超时
}
<think>首先,用户的问题是关于在C# .NET Framework 4.8环境下使用HttpClient实现文件数据上传和下载的方法。用户提供了几个引用,我需要参考这些引用,但根据系统指令,我也要参考用户上一次的问题和我的回答。不过,用户没有提供之前的对话历史,所以我只能基于当前问题处理。 系统指令包括: - 所有行内数学表达式使用$...$格式(但在这个上下文中,可能不涉及数学,所以我不用担心)。 - 独立公式使用$$...$$格式(同样,可能不适用)。 - LaTeX语法正确(主要是数学表达式)。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如:[^1]。 用户的问题是关于HttpClient使用,所以我需要构建一个清晰的回答,帮助用户实现文件上传和下载。 关键点: - 环境:.NET Framework 4.8。 - 工具:HttpClient。 - 任务:文件上传和下载。 从引用中: - 引用[1]:描述HttpClient是基于消息处理管道的HTTP客户端库。 - 引用[2]:提到.NET Framework 4.8有新的HTTP功能,包括HttpClient的改进。 - 引用[3]:强调推荐使用HttpClientFactory替代直接使用HttpClient,以避免常见陷阱。 - 引用[4]:提到WebClient类,但用户指定了HttpClient,所以我应该专注于HttpClient。 用户的问题是针对.NET 4.8,并且引用[3]提到HttpClientFactory是更好的选择。但在.NET Framework 4.8中,HttpClientFactory是ASP.NET Core的一部分,不是原生支持。.NET Framework 4.8主要是桌面应用,而HttpClientFactory更常用于ASP.NET Core。所以,我需要澄清这一点。 在.NET Framework 4.8中,HttpClient可以直接使用,但为了避免资源泄漏等问题,建议使用特定模式或HttpClientFactory if available. 然而,HttpClientFactory是.NET Core和更高版本的特性。对于.NET Framework 4.8,开发者通常手动管理HttpClient使用单例模式。 用户的具体需求: - 文件上传:可能使用MultipartFormDataContent。 - 文件下载:使用GetStreamAsync或类似方法。 回答结构: - 系统指令要求回答结构清晰,逐步解决问题。 - 使用中文回答。 - 在回答中引用时添加引用标识。 - 最后生成相关问题。 大纲回答: 1. **介绍HttpClient.NET 4.8中的使用**:解释注意事项,推荐最佳实践。 2. **文件上传方法**: - 使用HttpClient的PostAsync方法。 - 构建MultipartFormDataContent包含文件。 - 示例代码。 3. **文件下载方法**: - 使用HttpClient的GetAsync或GetStreamAsync。 - 保存文件到本地。 - 示例代码。 4. **注意事项**:讨论HttpClientFactory的建议,但在.NET 4.8中的替代方案。 5. **生成相关问题**:在结尾添加相关问题。 引用处理: - 在回答中,当我引用提供的引用时,在段落末尾添加[^数字]。 - 例如,提到推荐使用HttpClientFactory时,引用[^3]。 - 提到HttpClient的改进时,引用[^2]。 示例代码: - 由于是代码相关,我可以提供C#代码片段。 - 系统指令提到在示例中使用分隔符,如“```python”在快速排序例子中,所以我可以使用“```csharp”用于代码。 确保回答真实可靠: - 基于我的知识: - 上传文件:使用MultipartFormDataContent。 - 下载文件:使用HttpClient.GetStreamAsync() and copy to a file. - 在.NET 4.8中,HttpClient可以用,但注意singleton pattern to avoid socket exhaustion. 最后,生成相关问题: - 根据系统指令“§§相关问题§§”,列出几个相关问题。 - 例如:如何避免HttpClient的资源泄漏?HttpClientFactory在.NET 4.8中如何使用?性能优化提示等。 完整回答结构: - 开头:简介。 - 文件上传部分。 - 文件下载部分。 - 注意事项。 - 结尾:相关问题。 </think>### 在 .NET Framework 4.8 中使用 HttpClient 实现文件上传和下载的方法 在 .NET Framework 4.8 环境下,HttpClient 是处理 HTTP 请求的推荐工具,它基于现代异步模型,支持高效的文件数据传输[^1][^2]。本文将为用户逐步讲解文件上传和下载的实现方法。值得注意的是,在 .NET 开发中,直接创建 HttpClient 实例可能导致资源泄漏等问题,官方建议使用 HttpClientFactory 来管理生命周期(特别是在 ASP.NET Core 应用中),但在 .NET Framework 4.8 中,HttpClientFactory 并非原生支持[^3]。因此,我们将采用单例模式或手动管理 HttpClient 实例来避免端口耗尽(socket exhaustion)问题。以下实现基于标准 .NET 4.8 控制台或 WinForms 应用,确保代码简洁、可靠。 #### 步骤 1: 设置 HttpClient 实例(推荐单例模式) 在 .NET 4.8 中,HttpClient 应被设计为长时间存活的对象,以避免频繁创建销毁造成的性能问题。以下是初始化示例: ```csharp using System.Net.Http; // 使用静态实例确保 HttpClient 重用(单例模式) public static class HttpClientSingleton { public static readonly HttpClient Instance = new HttpClient(); // 可选:设置超时或其他配置(例如默认请求头) static HttpClientSingleton() { Instance.Timeout = TimeSpan.FromSeconds(30); } } ``` - **为什么单例?** 每次创建新 HttpClient 实例会占用系统资源,可能导致端口耗尽错误[^3]。在 .NET 4.8 中,没有内置的 HttpClientFactory,因此手动管理是常见做法[^2]。 #### 步骤 2: 实现文件上传(使用 MultipartFormDataContent) 文件上传通常通过 HTTP POST 方法实现,使用 MultipartFormDataContent 包装文件数据。以下是完整示例方法: ```csharp using System; using System.IO; using System.Net.Http; using System.Threading.Tasks; public async Task UploadFileAsync(string filePath, string uploadUrl) { try { // 使用单例 HttpClient 实例 var httpClient = HttpClientSingleton.Instance; // 创建多部分表单内容 using (var content = new MultipartFormDataContent()) { // 读取文件并添加到表单 var fileBytes = await File.ReadAllBytesAsync(filePath); var fileContent = new ByteArrayContent(fileBytes); content.Add(fileContent, "file", Path.GetFileName(filePath)); // "file" 是表单字段名 // 发送 POST 请求 var response = await httpClient.PostAsync(uploadUrl, content); response.EnsureSuccessStatusCode(); // 检查响应状态(200-299) Console.WriteLine($"文件上传成功!响应: {await response.Content.ReadAsStringAsync()}"); } } catch (Exception ex) { Console.WriteLine($"上传失败: {ex.Message}"); } } ``` - **关键点**: - `MultipartFormDataContent` 用于处理文件数据和其他表单字段。 - `ByteArrayContent` 将文件转换为字节数组传输。 - `EnsureSuccessStatusCode()` 确保 HTTP 响应成功(例如 200 OK)。 - 这个方法适用于上传文件到任何支持接收文件的服务器端 API(如 ASP.NET Web API)。 - **注意事项**:文件较大时,建议添加进度监控(如使用 `ProgressMessageHandler` 或自定义回调)。 #### 步骤 3: 实现文件下载(使用 GetStreamAsync) 文件下载通过 HTTP GET 方法实现,使用流读取响应数据并保存到本地文件。以下是完整示例方法: ```csharp using System; using System.IO; using System.Net.Http; using System.Threading.Tasks; public async Task DownloadFileAsync(string downloadUrl, string savePath) { try { // 使用单例 HttpClient 实例 var httpClient = HttpClientSingleton.Instance; // 发送 GET 请求并获取响应流 using (var response = await httpClient.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead)) { response.EnsureSuccessStatusCode(); // 读取响应流并写入文件 using (var stream = await response.Content.ReadAsStreamAsync()) using (var fileStream = new FileStream(savePath, FileMode.Create, FileAccess.Write)) { await stream.CopyToAsync(fileStream); } Console.WriteLine($"文件下载成功!保存路径: {savePath}"); } } catch (Exception ex) { Console.WriteLine($"下载失败: {ex.Message}"); } } ``` - **关键点**: - `GetAsync` 使用 `HttpCompletionOption.ResponseHeadersRead` 选项实现异步流处理,避免内存溢出(尤其在下载大文件时)。 - `CopyToAsync` 方法高效地将响应流复制到本地文件流。 - 这个方法适用于从远程服务器下载文件(如从 API 或静态资源 URL)。 - **优化提示**:对于超大文件,可以添加分段下载支持(如 Range 请求)。 #### 注意事项和最佳实践 1. **资源管理**:HttpClient 实现了 IDisposable,但在单例模式下,不需要手动 Dispose(除非应用退出)。如果创建多个实例,确保在使用后调用 Dispose 以避免内存泄漏[^3]。 2. **性能优化**:.NET Framework 4.8 提供新的异步 IO 模型,HttpClient 充分利用了这一点,支持高并发操作[^2]。上传/下载大文件时,考虑设置 `Timeout` 或使用 CancellationToken。 3. **错误处理**:添加重试逻辑(如使用 Polly 库)来应对网络波动。 4. **与 HttpClientFactory 对比**:在 .NET Core 或 .NET 5+ 中,优先使用 HttpClientFactory 来管理实例池,但在 .NET 4.8 中,可以通过第三方库(如 Microsoft.Extensions.Http)或自定义实现类似功能[^3]。 5. **安全性**:确保使用 HTTPS 传输敏感文件数据,避免中间人攻击。 通过以上方法,您可以在 .NET 4.8 应用中高效处理文件上传和下载。实际部署时,测试网络边界条件(如带宽限制)以确保可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值