-
C#网格编程之HTTP编程(HttpClient、HttpListener)
第6章 C# HTTP编程实战
6.1 C# HTTP编程(HttpClient、HttpListener)
一、为什么要学这两个类?
我刚做C# HTTP开发时,只会用HttpClient发请求,直到需要写一个轻量级HTTP服务器,才发现HttpListener的好用——不需要搭ASP.NET Core,几行代码就能写一个能处理请求的服务器。后来又踩了HttpClient的坑:每次请求都创建新实例,导致系统端口耗尽,程序崩溃。这节我把自己踩过的坑、实战经验都揉vb.net教程C#教程python教程SQL教程access 2010教程
进去,用大白话讲透HttpClient和HttpListener的用法,结合代码逐行讲解,让你既能高效发HTTP请求,又能快速写HTTP服务器。
二、HttpClient:C#发HTTP请求的“瑞士军刀”
HttpClient是.NET官方封装的HTTP客户端,不管是GET、POST、PUT、DELETE,还是HTTPS、CORS、代理,它都能搞定——就像你用手机浏览器上网,不需要懂HTTP协议的细节,直接输入网址就行。
-
先搞懂HttpClient的核心坑:不要每次请求都创建!
我刚学HttpClient时,每次请求都new HttpClient(),结果程序跑了几个小时就崩溃了——后来才知道:HttpClient会创建底层的HttpMessageHandler,而HttpMessageHandler会占用系统端口,每次创建HttpClient都会新建一个HttpMessageHandler,导致端口耗尽。
正确做法:全局单例HttpClient,或者用IHttpClientFactory(推荐)。
错误示例:
csharp
// 错误:每次请求都创建HttpClient,会耗尽端口
foreach (var url in urls)
{
using var httpClient = new HttpClient();
var response = await httpClient.GetAsync(url);
}
正确示例(全局单例):
csharp
// 正确:全局单例HttpClient,整个程序只创建一次
public static class HttpClientHelper
{
public static readonly HttpClient Instance = new HttpClient();
}
// 用法
var response = await HttpClientHelper.Instance.GetAsync(url);
-
C#实战:用HttpClient发送各种HTTP请求
(1)发送GET请求:获取JSON数据并解析
csharp
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.Text.Json;
namespace HttpClientGetDemo;
class Program
{
// 全局单例HttpClient
private static readonly HttpClient _httpClient = new HttpClient();
static async Task Main(string[] args)
{
try
{
// 1. 发送GET请求
string url = "https://jsonplaceholder.typicode.com/users/1";
HttpResponseMessage response = await _httpClient.GetAsync(url);
// 2. 检查请求是否成功:状态码不是2xx就抛出异常
response.EnsureSuccessStatusCode();
// 3. 读取响应体:把JSON字符串转换成对象
string jsonString = await response.Content.ReadAsStringAsync();
User user = JsonSerializer.Deserialize<User>(jsonString) ?? new User();
// 4. 输出结果
Console.WriteLine($"用户ID:{user.Id}");
Console.WriteLine($"用户名:{user.Name}");
Console.WriteLine($"邮箱:{user.Email}");
}
catch (HttpRequestException ex)
{
Console.WriteLine($"请求失败:{ex.Message}");
}
}
// 定义和JSON对应的实体类
public class User
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
}
}
代码逐行讲:
1.private static readonly HttpClient _httpClient = new HttpClient();:全局单例HttpClient,整个程序只创建一次,避免端口耗尽;
2.await _httpClient.GetAsync(url);:异步发送GET请求,返回HttpResponseMessage,包含状态码、响应头、响应体;
3.response.EnsureSuccessStatusCode();:如果状态码不是2xx(比如404、500),抛出HttpRequestException,方便统一处理错误;
4.await response.Content.ReadAsStringAsync();:读取响应体的字符串内容,response.Content是HttpContent类型,还可以用ReadAsByteArrayAsync()(读取字节数组)、ReadAsStreamAsync()(读取流);
5.JsonSerializer.Deserialize
(2)发送POST请求:提交JSON数据
csharp
using System;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
namespace HttpClientPostDemo;
class Program
{
private static readonly HttpClient _httpClient = new HttpClient();
static async Task Main(string[] args)
{
try
{
// 1. 准备要提交的数据
var newUser = new User
{
Name = "张三",
Email = "zhangsan@example.com"
};
// 2. 发送POST请求:自动把对象转换成JSON,设置Content-Type为application/json
string url = "https://jsonplaceholder.typicode.com/users";
HttpResponseMessage response = await _httpClient.PostAsJsonAsync(url, newUser);
response.EnsureSuccessStatusCode();
// 3. 读取响应体:获取创建后的用户信息
User createdUser = await response.Content.ReadFromJsonAsync<User>() ?? new User();
Console.WriteLine($"创建的用户ID:{createdUser.Id}");
}
catch (HttpRequestException ex)
{
Console.WriteLine($"请求失败:{ex.Message}");
}
}
public class User
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
}
}
代码关键:
PostAsJsonAsync(url, newUser):自动把newUser对象序列化成JSON,设置Content-Type为application/json,不需要手动处理JSON序列化;
ReadFromJsonAsync
(3)发送PUT请求:更新数据
csharp
// 准备更新后的数据
var updatedUser = new User
{
Id = 1,
Name = "李四",
Email = "lisi@example.com"
};
// 发送PUT请求
string url = "https://jsonplaceholder.typicode.com/users/1";
HttpResponseMessage response = await _httpClient.PutAsJsonAsync(url, updatedUser);
response.EnsureSuccessStatusCode();
(4)发送DELETE请求:删除数据
csharp
// 发送DELETE请求
string url = "https://jsonplaceholder.typicode.com/users/1";
HttpResponseMessage response = await _httpClient.DeleteAsync(url);
response.EnsureSuccessStatusCode();
(5)发送带Header的请求:身份验证、自定义Header
csharp
// 创建请求消息,设置Header
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.example.com/users/1");
request.Headers.Add("Authorization", "Bearer your-token-here"); // 身份验证Header
request.Headers.Add("X-Custom-Header", "custom-value"); // 自定义Header
// 发送请求
HttpResponseMessage response = await _httpClient.SendAsync(request);
-
进阶:用IHttpClientFactory管理HttpClient(推荐)
IHttpClientFactory是.NET Core 2.1+引入的,专门用来管理HttpClient的生命周期——它会自动复用底层的HttpMessageHandler,避免端口耗尽,还能方便地配置超时、代理、Header等。
步骤1:注册IHttpClientFactory
在Program.cs中注册:
csharp
var builder = WebApplication.CreateBuilder(args);
// 注册IHttpClientFactory
builder.Services.AddHttpClient();
var app = builder.Build();
步骤2:注入并使用
在控制器或服务中注入:
csharp
public class UserService
{
private readonly IHttpClientFactory _httpClientFactory;
// 构造函数注入IHttpClientFactory
public UserService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task<User> GetUserAsync(int id)
{
// 创建HttpClient
var httpClient = _httpClientFactory.CreateClient();
string url = $"https://jsonplaceholder.typicode.com/users/{id}";
HttpResponseMessage response = await httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<User>() ?? new User();
}
}
三、HttpListener:轻量级HTTP服务器的“神器”
HttpListener是.NET自带的轻量级HTTP服务器,不需要搭ASP.NET Core,几行代码就能写一个能处理HTTP请求的服务器——适合写工具类程序、测试服务器、轻量级API。
注意:HttpListener需要管理员权限才能运行(因为要绑定端口),或者用netsh命令注册URL:
bash
netsh http add urlacl url=http://+:8080/ user=DOMAINusername
-
C#实战:用HttpListener写一个简单的HTTP服务器
csharp
using System;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace HttpListenerDemo;
class Program
{
static async Task Main(string[] args)
{
// 1. 创建HttpListener实例
using var listener = new HttpListener();
// 2. 添加要监听的URL前缀:http://+:8080/ 表示监听所有IP的8080端口
listener.Prefixes.Add("http://localhost:8080/");
listener.Prefixes.Add("http://127.0.0.1:8080/");
try
{
// 3. 启动服务器
listener.Start();
Console.WriteLine("HTTP服务器已启动,监听端口8080...");
while (true)
{
// 4. 异步等待客户端请求:GetContextAsync会阻塞当前线程,直到有请求进来
HttpListenerContext context = await listener.GetContextAsync();
// 5. 处理请求:用Task.Run避免阻塞主线程,同时处理多个请求
_ = Task.Run(() => ProcessRequest(context));
}
}
catch (Exception ex)
{
Console.WriteLine($"服务器异常:{ex.Message}");
}
finally
{
// 6. 停止服务器
listener.Stop();
}
}
// 处理单个HTTP请求
private static async Task ProcessRequest(HttpListenerContext context)
{
// 1. 获取请求和响应对象
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
try
{
Console.WriteLine($"收到请求:{request.HttpMethod} {request.Url}");
// 2. 根据请求路径和方法处理业务逻辑
string responseString = string.Empty;
if (request.HttpMethod == "GET" && request.Url?.AbsolutePath == "/")
{
responseString = "欢迎访问HTTP服务器!";
}
else if (request.HttpMethod == "GET" && request.Url?.AbsolutePath.StartsWith("/users/") == true)
{
// 解析用户ID:比如/users/1,提取1
string id = request.Url.AbsolutePath.Split('/')[2];
responseString = $"用户ID:{id},姓名:张三";
}
else
{
// 404 Not Found
response.StatusCode = (int)HttpStatusCode.NotFound;
responseString = "404 页面不存在";
}
// 3. 把响应字符串转换成字节数组
byte[] buffer = Encoding.UTF8.GetBytes(responseString);
// 4. 设置响应头
response.ContentLength64 = buffer.Length;
response.ContentType = "text/plain; charset=utf-8";
// 5. 把响应写入输出流
await response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
}
catch (Exception ex)
{
Console.WriteLine($"处理请求异常:{ex.Message}");
response.StatusCode = (int)HttpStatusCode.InternalServerError;
byte[] errorBuffer = Encoding.UTF8.GetBytes("500 服务器内部错误");
response.ContentLength64 = errorBuffer.Length;
await response.OutputStream.WriteAsync(errorBuffer, 0, errorBuffer.Length);
}
finally
{
// 6. 关闭响应输出流,释放资源
response.OutputStream.Close();
}
}
}
代码逐行讲:
1.new HttpListener();:创建HttpListener实例;
2.listener.Prefixes.Add("http://localhost:8080/");:添加要监听的URL前缀,必须以/结尾——比如http://localhost:8080/表示监听http://localhost:8080下的所有请求;
3.listener.Start();:启动服务器,开始监听请求;
4.HttpListenerContext context = await listener.GetContextAsync();:异步等待客户端请求,GetContextAsync会阻塞当前线程,直到有请求进来;
5._ = Task.Run(() => ProcessRequest(context));:用Task.Run把请求处理逻辑放到后台线程,避免阻塞主线程,这样服务器可以同时处理多个请求;
6.HttpListenerRequest request = context.Request;:获取请求对象,包含请求方法、URL、Header、请求体等;
7.HttpListenerResponse response = context.Response;:获取响应对象,用于设置状态码、Header、响应体等;
8.response.ContentLength64 = buffer.Length;:必须设置响应体的长度,否则客户端可能无法正确接收响应;
9.await response.OutputStream.WriteAsync(buffer, 0, buffer.Length);:把响应体写入输出流;
10.response.OutputStream.Close();:关闭输出流,释放资源——必须关闭,否则客户端会一直等待响应。
2. 进阶:用HttpListener处理JSON请求和响应
csharp
// 处理POST请求,接收JSON数据
if (request.HttpMethod == "POST" && request.Url?.AbsolutePath == "/users")
{
// 读取请求体的JSON数据
using var reader = new System.IO.StreamReader(request.InputStream, request.ContentEncoding);
string jsonString = await reader.ReadToEndAsync();
User newUser = JsonSerializer.Deserialize<User>(jsonString) ?? new User();
// 模拟保存到数据库,生成ID
newUser.Id = 100;
// 返回JSON响应
response.ContentType = "application/json; charset=utf-8";
string responseJson = JsonSerializer.Serialize(newUser);
byte[] buffer = Encoding.UTF8.GetBytes(responseJson);
response.ContentLength64 = buffer.Length;
await response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
}
四、HttpClient vs HttpListener:怎么选?
场景 选哪个? 原因
发送HTTP请求 HttpClient .NET官方封装,功能强大,用法简单
写轻量级HTTP服务器 HttpListener 不需要搭ASP.NET Core,几行代码就能跑
写企业级API服务器 ASP.NET Core 性能更高,支持路由、中间件、依赖注入等
五、基础知识拓展
-
怎么设置HttpClient的超时时间?
默认超时时间是100秒,可以通过Timeout属性设置:
csharp
_httpClient.Timeout = TimeSpan.FromSeconds(30); // 设置超时时间为30秒
如果需要无限超时(不推荐),可以设置为Timeout.InfiniteTimeSpan。 -
怎么处理HTTPS的自签名证书?
如果服务器用的是自签名证书(比如测试环境),HttpClient会验证失败,需要跳过证书验证:
csharp
var handler = new HttpClientHandler();
// 跳过证书验证(生产环境不要这么做!)
handler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true;
using var httpClient = new HttpClient(handler);
-
怎么用HttpListener处理CORS?
在响应头中添加Access-Control-Allow-Origin:
csharp
response.Headers.Add("Access-Control-Allow-Origin", "*"); // 允许所有域名访问
response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Authorization");
六、总结:C# HTTP编程的两个核心工具
HttpClient:发HTTP请求的首选,全局单例或用IHttpClientFactory,避免端口耗尽;
HttpListener:写轻量级HTTP服务器的神器,不需要搭ASP.NET Core,适合工具类程序、测试服务器;
进阶:企业级API服务器用ASP.NET Core,性能更高,生态更完善。
本站原创,转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49519.html










