想象你要点外卖
传统方式(像打电话订餐):
csharp
scss
// 每次都要:找餐厅电话→打电话→等接通→点餐→等确认→挂电话
// 想再点一杯饮料?重新打一遍电话!
public void 传统点餐()
{
// 第一次:点主食
找电话号码("肯德基");
打电话("我要一个汉堡");
等待接通();
说明需求();
等待确认();
挂断电话();
// 第二次:点饮料(又要重复所有步骤)
找电话号码("肯德基");
打电话("再加一杯可乐");
等待接通();
说明需求();
等待确认();
挂断电话();
// 第三次:查订单状态(还得再打)
找电话号码("肯德基");
打电话("我的餐到哪了");
// ... 效率极低!
}
使用HttpClient(像用外卖APP):
csharp
dart
// 就像在美团APP里操作
HttpClient 我的外卖APP = new HttpClient();
// 一次设置,多次使用
我的外卖APP.BaseAddress = new Uri("https://api.kfc.com/");
async Task 智能点餐()
{
// 点主食(像在APP里选商品)
var 汉堡订单 = await 我的外卖APP.PostAsync("order", 汉堡数据);
// 加饮料(直接在订单里添加,不用重新打开)
var 饮料订单 = await 我的外卖APP.PostAsync("order/add", 可乐数据);
// 查进度(刷新一下就行)
var 订单状态 = await 我的外卖APP.GetAsync("order/status");
// 所有操作都在同一个APP里完成,不用反复"打电话"
}
HttpClient的核心功能(像外卖APP的特性)
1. 连接复用 - 不用反复登录
csharp
scss
// ❌ 传统:每次都要重新登录
public void 传统方式()
{
// 点餐
打开APP();
登录();
点餐();
退出APP();
// 查订单(又要重新登录)
打开APP();
登录();
查订单();
退出APP();
}
// ✅ HttpClient:一次登录,持续使用
public class 我的外卖服务
{
private HttpClient 外卖APP = new HttpClient();
public 我的外卖服务()
{
// 只需要登录一次
外卖APP.BaseAddress = new Uri("https://外卖平台.com/");
外卖APP.DefaultRequestHeaders.Add("Authorization", "我的登录凭证");
}
public async Task 点餐() => await 外卖APP.PostAsync(...);
public async Task 查订单() => await 外卖APP.GetAsync(...);
public async Task 取消订单() => await 外卖APP.DeleteAsync(...);
// 所有操作都用同一个登录状态
}
2. 同时处理多个请求 - 并行点多家餐厅
csharp
csharp
public async Task 点周末大餐()
{
// 同时向多家餐厅下单
var 肯德基任务 = 我的外卖APP.PostAsync("kfc/order", 炸鸡数据);
var 麦当劳任务 = 我的外卖APP.PostAsync("mcdonald/order", 汉堡数据);
var 奶茶店任务 = 我的外卖APP.PostAsync("milktea/order", 奶茶数据);
// 等待所有餐厅接单(并行等待,效率高)
await Task.WhenAll(肯德基任务, 麦当劳任务, 奶茶店任务);
Console.WriteLine("所有订单已提交!");
}
3. 发送各种类型的数据 - 不只是文字
csharp
csharp
public async Task 提交订单()
{
// 1. 发送JSON数据(像填写详细的配送信息)
var 订单信息 = new
{
商品 = "香辣鸡腿堡套餐",
数量 = 1,
地址 = "北京市朝阳区xxx",
备注 = "不要辣椒,快点送"
};
var json内容 = new StringContent(
JsonSerializer.Serialize(订单信息),
Encoding.UTF8,
"application/json"); // 告诉服务器这是JSON格式
await 我的外卖APP.PostAsync("order", json内容);
// 2. 发送表单数据(像填写简单问卷)
var 表单数据 = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("rating", "5"),
new KeyValuePair<string, string>("comment", "很好吃")
});
await 我的外卖APP.PostAsync("feedback", 表单数据);
// 3. 上传文件(像发送发票照片)
using var 照片流 = File.OpenRead("发票.jpg");
var 文件内容 = new StreamContent(照片流);
var 多部分内容 = new MultipartFormDataContent
{
{ new StringContent("12345"), "orderId" },
{ 文件内容, "invoice", "发票.jpg" }
};
await 我的外卖APP.PostAsync("upload/invoice", 多部分内容);
}
完整的外卖APP示例
csharp
csharp
public class 智能外卖管家
{
private readonly HttpClient 我的APP;
public 智能外卖管家()
{
我的APP = new HttpClient
{
BaseAddress = new Uri("https://api.food-delivery.com/v1/"),
Timeout = TimeSpan.FromSeconds(30) // 30秒没响应就取消
};
// 设置APP的通用配置
我的APP.DefaultRequestHeaders.Add("User-Agent", "MyFoodApp/1.0");
我的APP.DefaultRequestHeaders.Add("X-API-Key", "my_secret_key");
}
// 搜索餐厅
public async Task<List<餐厅>> 搜索餐厅(string 关键词, string 地址)
{
string url = $"restaurants?keyword={关键词}&address={地址}";
var 响应 = await 我的APP.GetAsync(url);
响应.EnsureSuccessStatusCode(); // 如果失败就抛出异常
return await 响应.Content.ReadFromJsonAsync<List<餐厅>>();
}
// 下单
public async Task<订单结果> 下单(订单 新订单)
{
var 内容 = new StringContent(
JsonSerializer.Serialize(新订单),
Encoding.UTF8,
"application/json");
var 响应 = await 我的APP.PostAsync("orders", 内容);
if (!响应.IsSuccessStatusCode)
{
// 处理失败情况
var 错误信息 = await 响应.Content.ReadAsStringAsync();
throw new Exception($"下单失败:{错误信息}");
}
return await 响应.Content.ReadFromJsonAsync<订单结果>();
}
// 跟踪订单
public async Task<配送状态> 查看配送状态(string 订单号)
{
var 响应 = await 我的APP.GetAsync($"orders/{订单号}/tracking");
if (响应.StatusCode == HttpStatusCode.NotFound)
{
throw new Exception("订单不存在");
}
响应.EnsureSuccessStatusCode();
return await 响应.Content.ReadFromJsonAsync<配送状态>();
}
// 批量操作:收藏多家餐厅
public async Task 批量收藏餐厅(List<string> 餐厅ID列表)
{
var 任务列表 = new List<Task>();
foreach (var 餐厅ID in 餐厅ID列表)
{
var 任务 = 我的APP.PostAsync($"restaurants/{餐厅ID}/favorite", null);
任务列表.Add(任务);
}
// 同时发送所有收藏请求
await Task.WhenAll(任务列表);
Console.WriteLine("所有餐厅已收藏!");
}
}
实际使用场景
场景1:获取天气来决定点什么
csharp
csharp
public async Task 智能推荐()
{
// 先查天气
var 天气响应 = await 我的外卖APP.GetAsync("https://api.weather.com/北京");
var 天气数据 = await 天气响应.Content.ReadFromJsonAsync<天气信息>();
// 根据天气推荐
string 推荐餐品;
if (天气数据.温度 < 10)
推荐餐品 = "火锅";
else if (天气数据.温度 > 30)
推荐餐品 = "冷面";
else
推荐餐品 = "便当";
// 搜索推荐餐品
var 餐厅列表 = await 搜索餐厅(推荐餐品, "我家地址");
Console.WriteLine($"根据天气推荐:{推荐餐品}");
}
场景2:处理超时和重试
csharp
csharp
public async Task<订单结果> 稳定下单(订单 新订单)
{
int 重试次数 = 0;
int 最大重试次数 = 3;
while (重试次数 < 最大重试次数)
{
try
{
// 尝试下单
return await 下单(新订单);
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.RequestTimeout)
{
// 超时了,等一会再试
重试次数++;
Console.WriteLine($"请求超时,第{重试次数}次重试...");
if (重试次数 >= 最大重试次数)
throw new Exception("多次尝试失败,请检查网络");
await Task.Delay(1000 * 重试次数); // 等待时间递增
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.TooManyRequests)
{
// 服务器说请求太多了,等5秒再试
Console.WriteLine("服务器忙,等待5秒...");
await Task.Delay(5000);
}
}
throw new Exception("下单失败");
}
HttpClient的最佳实践
✅ 正确:像长期使用一个外卖APP
csharp
csharp
// 在整个应用生命周期中使用同一个HttpClient实例
public class 全局外卖服务
{
// 静态的,整个应用只创建一个
private static readonly HttpClient 我的长期APP = new HttpClient
{
BaseAddress = new Uri("https://api.food.com/"),
Timeout = TimeSpan.FromSeconds(30)
};
// 所有方法都使用这个实例
public async Task 点餐() => await 我的长期APP.PostAsync(...);
public async Task 查订单() => await 我的长期APP.GetAsync(...);
}
❌ 错误:像每次点餐都下载一个新APP
csharp
csharp
public void 错误的点餐方式()
{
for (int i = 0; i < 10; i++)
{
// 错误!每次都要创建新的HttpClient
var 临时APP = new HttpClient();
await 临时APP.GetAsync("https://api.food.com/order");
// 忘记释放,会导致资源泄露!
// 就像手机里装了10个相同的外卖APP
}
}
✅ 更好:使用IHttpClientFactory(像官方应用商店)
csharp
typescript
// 在Startup.cs中配置
services.AddHttpClient("外卖服务", client =>
{
client.BaseAddress = new Uri("https://api.food.com/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
});
// 使用时获取
public class 订单服务
{
private readonly IHttpClientFactory _app商店;
public 订单服务(IHttpClientFactory app商店)
{
_app商店 = app商店;
}
public async Task 处理订单()
{
// 从"应用商店"获取一个配置好的HttpClient
var 我的APP = _app商店.CreateClient("外卖服务");
// 使用它
await 我的APP.PostAsync("orders", ...);
// 不用操心释放,工厂会管理生命周期
}
}
常见问题解决方案
问题1:网络不稳定怎么办?
csharp
csharp
public async Task<string> 稳定获取菜单()
{
try
{
// 设置合理的超时时间
我的APP.Timeout = TimeSpan.FromSeconds(15);
// 使用CancellationToken可以中途取消
var 取消令牌源 = new CancellationTokenSource();
// 如果5秒还没响应,就取消
取消令牌源.CancelAfter(TimeSpan.FromSeconds(5));
var 响应 = await 我的APP.GetAsync("menu", 取消令牌源.Token);
return await 响应.Content.ReadAsStringAsync();
}
catch (TaskCanceledException)
{
return "请求超时,请稍后重试";
}
catch (HttpRequestException)
{
return "网络连接失败,请检查网络";
}
}
问题2:需要添加认证信息?
csharp
csharp
public void 设置登录状态(string 令牌)
{
// 方法1:添加到默认请求头
我的APP.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", 令牌);
// 方法2:每次请求单独添加
var 请求 = new HttpRequestMessage(HttpMethod.Get, "user/profile");
请求.Headers.Authorization = new AuthenticationHeaderValue("Bearer", 令牌);
// 方法3:使用HttpClientHandler
var handler = new HttpClientHandler
{
UseCookies = true,
CookieContainer = new CookieContainer()
};
handler.CookieContainer.Add(new Uri("https://api.food.com"),
new Cookie("session", 令牌));
}
总结比喻
HttpClient = 你的智能手机外卖APP
- 一次安装,长期使用 - 不用每次点餐都重新下载APP
- 保持登录状态 - 登录一次,后续操作都不用再登录
- 并行操作 - 可以同时浏览餐厅、查看订单、联系客服
- 处理各种数据 - 能发文字、图片、文件,就像APP能发消息、传照片
- 智能重试 - 网络不好时会自动重试,就像APP的"重新加载"
- 统一管理 - 所有外卖操作都在一个APP里完成
核心思想转变:
从「每次都要重新打电话联系餐厅」
变成「有一个智能外卖APP,所有餐厅、所有操作都在里面完成」
HttpClient让你的程序能像使用外卖APP一样,高效、稳定地与各种网络服务通信!