Sage.Http
2.0.0.11
dotnet add package Sage.Http --version 2.0.0.11
NuGet\Install-Package Sage.Http -Version 2.0.0.11
<PackageReference Include="Sage.Http" Version="2.0.0.11" />
<PackageVersion Include="Sage.Http" Version="2.0.0.11" />
<PackageReference Include="Sage.Http" />
paket add Sage.Http --version 2.0.0.11
#r "nuget: Sage.Http, 2.0.0.11"
#:package Sage.Http@2.0.0.11
#addin nuget:?package=Sage.Http&version=2.0.0.11
#tool nuget:?package=Sage.Http&version=2.0.0.11
Sage.Http
开源地址: https://gitee.com/liupenglai/sage.http
项目简介
一个简洁、可扩展、支持 DI 与独立模式的 HttpClient 管理库,提供统一配置、稳健的弹性策略(重试、熔断、限流、超时)、事件监控、消息处理器与拦截器,并兼顾线程安全、内存安全与 AOT 友好。
特性
- 统一管理
HttpClient:支持 ASP.NET Core(DI)与独立模式(WinForms/WPF/控制台) - 流式
HttpRequestBuilder:简洁链式 API,内置 JSON 序列化/反序列化,AOT 友好重载 - 弹性策略:重试、熔断、限流、超时,支持事件观测与订阅
- 消息处理器与拦截器:推荐工厂重载注册,可灵活注入运行时参数;内置
LoggingHandler、BearerTokenHandler、CorrelationIdHandler等 - 配置即用:JSON 配置(
appsettings.json)与环境差异化配置,优雅加载到HttpClientOptions - 事件监控:
RetryEvent、CircuitBreakerEvent、RateLimiterEvent等,支持弱引用订阅,避免泄漏 - 高性能与安全:基于
SocketsHttpHandler的连接池/HTTP/2,多线程安全,关注内存与线程开销 - 易扩展:自定义处理器、拦截器、事件处理,可与现有日志/监控系统集成
安装
dotnet add package Sage.Http
快速开始
ASP.NET Core(DI 模式)
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClientManager();
builder.Services.AddHttpClient("github", options =>
{
options.BaseAddress = new Uri("https://api.github.com");
options.Timeout = TimeSpan.FromSeconds(30);
options.AddDefaultHeader("User-Agent", "SageHttpDemo/1.0");
options.ConfigureRetry(r =>
{
r.MaxRetryAttempts = 3;
r.BaseDelay = TimeSpan.FromSeconds(2);
r.BackoffType = BackoffType.Exponential;
r.RetryOnStatusCodes = new HashSet<int> { 408, 429, 500, 502, 503, 504 };
});
options.ConfigureCircuitBreaker(cb =>
{
cb.FailureRatio = 0.5;
cb.SamplingDuration = TimeSpan.FromSeconds(30);
cb.MinimumThroughput = 5;
cb.BreakDuration = TimeSpan.FromMinutes(1);
});
// LoggingHandler(工厂方式自定义级别与体开关)
options.AddHandler(sp => new LoggingHandler(
sp.GetRequiredService<ILogger<LoggingHandler>>(),
requestLogLevel: LogLevel.Information,
responseLogLevel: LogLevel.Information,
logRequestBody: false,
logResponseBody: false));
});
WinForms/控制台(独立模式)
var loggerFactory = LoggerFactory.Create(b =>
{
b.AddConsole();
b.SetMinimumLevel(LogLevel.Information);
});
var httpLogger = loggerFactory.CreateLogger<LoggingHandler>();
var manager = StandaloneHttpClientManager.Create(config =>
{
config.RegisterClient("api", options =>
{
options.SetBaseAddress("https://jsonplaceholder.typicode.com")
.SetTimeout(TimeSpan.FromSeconds(30))
.ConfigureRetry(r => { r.MaxRetryAttempts = 3; r.BaseDelay = TimeSpan.FromSeconds(1); });
options.AddHandler(_ => new LoggingHandler(httpLogger,
requestLogLevel: LogLevel.Information,
responseLogLevel: LogLevel.Information,
logRequestBody: false,
logResponseBody: false));
options.AddRequestInterceptor(req =>
{
req.Headers.TryAddWithoutValidation("X-Trace-Id", Guid.NewGuid().ToString("N"));
});
});
});
两种调用方式(并列示例)
原生 HttpClient 与链式 HttpRequestBuilder 均可无缝使用:
// 原生 HttpClient
var client = manager.GetClient("jsonplaceholder");
var post = await client.GetFromJsonAsync<Post>("/posts/1");
await client.PostAsJsonAsync("/posts", new { title = "demo" });
// 链式 HttpRequestBuilder
var posts = await manager
.CreateRequest("jsonplaceholder", "/posts")
.WithQuery("_limit", "10")
.WithHeader("X-Trace-Id", Guid.NewGuid().ToString("N"))
.GetFromJsonAsync<Post[]>();
// AOT 友好反序列化(JsonTypeInfo<T> 源生成)
var p1 = await manager
.CreateRequest("jsonplaceholder", "/posts/1")
.GetFromJsonAsync(typeInfo!);
请求体与表单
- JSON(AOT 友好):使用
JsonTypeInfo<T>源生成上下文
var order = new Order { ItemId = 42, Qty = 1 };
// 链式:WithJsonBody + PostAsync
await manager
.CreateRequest("api", "/orders")
.WithJsonBody(order, AppJsonContext.Default.Order)
.PostAsync();
// 便捷:PostAsJsonAsync
await manager
.CreateRequest("api", "/orders")
.PostAsJsonAsync(order, AppJsonContext.Default.Order);
- 表单(AOT 友好):使用
PostAsFormAsync
var form = new Dictionary<string, string> {
["username"] = "admin",
["password"] = "123456"
};
await manager
.CreateRequest("api", "/login")
.PostAsFormAsync(form);
- 文件上传(Multipart)
using var fs = File.OpenRead("c:/tmp/demo.png");
await manager
.CreateRequest("api", "/upload")
.WithFormBody(new Dictionary<string,string> { ["note"] = "hello" })
.WithFile("file", fs, fileName: "demo.png", contentType: "image/png")
.PostAsync();
- 注意:
PostAsJsonAsync/…AsFormAsync不能与WithXxxBody混用,否则抛出InvalidOperationException。WithFile仅在 Multipart 模式有效;若先使用WithFormBody,添加文件会自动切换到 Multipart。
响应验证与错误处理
ValidateAsync:仅校验状态码是否为 2xx;失败抛出HttpResponseException或泛型HttpResponseException<TError>。ValidateResponseAsync:校验后返回原始HttpResponseMessage,便于链式继续解析内容。- AOT 提示:
ValidateAsync<TError>/ValidateResponseAsync<TError>仅提供JsonSerializerOptions重载,标注为不支持 AOT。- AOT 场景优先使用非泛型
ValidateAsync()/ValidateResponseAsync();如需错误对象,建议手动读取正文并使用源生成上下文解析:
// 仅验证(非 2xx 将抛出 HttpResponseException)
await manager
.CreateRequest("jsonplaceholder", "/posts/404")
.GetAsync()
.ValidateAsync();
// 验证并返回响应以继续解析
var response = await manager
.CreateRequest("jsonplaceholder", "/posts/1")
.GetAsync()
.ValidateResponseAsync();
var post = await response.Content.ReadFromJsonAsync<Post>();
// AOT 场景:手动解析错误对象
var bad = await manager
.CreateRequest("api", "/orders/unknown")
.GetAsync();
await bad.ValidateResponseAsync(); // 先校验状态码
var json = await bad.Content.ReadAsStringAsync();
var err = JsonSerializer.Deserialize<MyError>(json, AppJsonContext.Default.MyError);
// 直接链式读取(自动校验状态码)
var p1 = await manager
.CreateRequest("api", "/posts/1")
.GetAsync()
.ReadFromJsonAsync(AppJsonContext.Default.Post);
var text = await manager
.CreateRequest("api", "/posts/1")
.GetAsync()
.ReadAsStringAsync();
var bytes = await manager
.CreateRequest("api", "/image")
.GetAsync()
.ReadAsByteArrayAsync();
using var stream = await manager
.CreateRequest("api", "/file")
.GetAsync()
.ReadAsStreamAsync();
配置概览
HttpClientOptions- 基本:
BaseAddress、Timeout、HttpVersion、DefaultHeaders、UseCookies、AllowAutoRedirect等 - 连接:通过
ConfigurePrimaryHandler使用SocketsHttpHandler(连接池、Keep-Alive、HTTP/2 多路复用、ConnectTimeout)
- 基本:
- 弹性策略(
ResilienceOptions)- 重试:
MaxRetryAttempts、BaseDelay、BackoffType、UseJitter;基于状态码/范围/异常/ShouldRetry - 熔断:
FailureRatio、SamplingDuration、MinimumThroughput、BreakDuration - 限流:
PermitLimit、Window、QueueLimit - 超时:客户端级与策略级均可配置
- 事件:启用后可订阅
RetryEvent、CircuitBreakerEvent、RateLimiterEvent
- 重试:
消息处理器与拦截器
- 处理器(推荐在 DI 模式):
LoggingHandler、CorrelationIdHandler、BearerTokenHandler、CustomHeaderHandler、UserAgentHandler - 注册方式
- 泛型:
options.AddHandler<THandler>()(构造参数由 DI 解析,适合无自定义运行时参数) - 工厂:
options.AddHandler(sp => new Handler(...))(推荐,可自定义参数,DI 与独立模式均适用)
- 泛型:
- 拦截器:
AddRequestInterceptor、AddResponseInterceptorAsync、AddErrorInterceptor;更轻量,适合观测与简单变更 - 提示:避免同时用拦截器与
LoggingHandler记录同类日志,否则可能重复输出
JSON 配置示例(appsettings.json)
{
"HttpClients": {
"github": {
"BaseAddress": "https://api.github.com",
"Timeout": "00:00:30",
"HttpVersion": "2.0",
"DefaultHeaders": { "User-Agent": "SageHttpDemo/1.0" },
"Resilience": {
"EnableRetry": true,
"Retry": { "MaxRetryAttempts": 3, "BaseDelay": "00:00:02", "BackoffType": "Exponential" },
"EnableCircuitBreaker": true,
"CircuitBreaker": { "FailureRatio": 0.5, "SamplingDuration": "00:00:30", "MinimumThroughput": 5, "BreakDuration": "00:01:00" },
"EnableEvents": true
}
}
}
}
加载:builder.Services.AddHttpClient("github", builder.Configuration.GetSection("HttpClients:github").Get<HttpClientOptions>());
事件订阅
// 特定客户端
_manager.Subscribe<RetryEvent>("github", e => Debug.WriteLine($"Retry {e.AttemptNumber}: {e.RequestUri}"));
// 所有客户端
_manager.Subscribe<HttpClientEvent>(e => Console.WriteLine($"{e.GetType().Name}: {e.ClientName}"));
// 弱引用(避免内存泄漏)
_manager.Subscribe<RetryEvent>("api", OnRetry, useWeakReference: true);
使用场景
- 第三方 API:启用重试与
LoggingHandler,便于排查问题 - 微服务:
BearerTokenHandler+CorrelationIdHandler+ 熔断与重试 - 批量下载:限流(并发与队列限制)、长超时、响应流式读取
性能与故障排查
- 建议使用
GetClient复用实例;避免在循环中频繁CreateClient - 启用 HTTP/2 与连接池参数优化(
SocketsHttpHandler) - 日志排查:
LoggingHandler的logRequestBody/logResponseBody仅在调试场景启用,注意敏感信息脱敏 - 常见问题与更多排查步骤详见
Troubleshooting.md
文档导航
示例
许可证
Apache-2.0
本项目采用 Apache License 2.0 开源协议。详情见 LICENSE.txt。
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.DependencyInjection (>= 10.0.2)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.2)
- Microsoft.Extensions.Http (>= 10.0.2)
- Microsoft.Extensions.Http.Resilience (>= 10.2.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.2)
- Microsoft.Extensions.Options (>= 10.0.2)
-
net9.0
- Microsoft.Extensions.DependencyInjection (>= 10.0.2)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.2)
- Microsoft.Extensions.Http (>= 10.0.2)
- Microsoft.Extensions.Http.Resilience (>= 10.2.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.2)
- Microsoft.Extensions.Options (>= 10.0.2)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Sage.Http:
| Package | Downloads |
|---|---|
|
Sage.CloudStorage.Qiniu
Sage.CloudStorage.Qiniu 是一个基于 .NET 平台的现代化七牛云存储 SDK,采用完全异步设计,提供了对七牛云对象存储、CDN 等服务的简单易用的 API 封装。该库基于 Sage.Http 构建,具有高性能、可扩展的七牛云服务访问能力,特别适合企业级应用和大文件处理场景。 ## 核心优势 - **现代化API设计**:完全异步,符合.NET最佳实践 - **模块化架构**:各组件职责明确,易于扩展和维护 - **丰富的事件机制**:提供上传进度通知和完成事件 - **智能上传策略**:自动选择最佳上传方式和分片大小 - **完善的错误处理**:提供详细的错误信息和恢复机制 ## 功能特性 - **完整的对象存储支持**:上传、下载、管理、删除等操作 - **高级上传功能**: - 智能分片上传(自动优化分片大小) - 断点续传支持 - 并发控制 - 实时进度监控 - **CDN管理**:刷新、预取、带宽查询、日志下载 - **数据处理**:图片处理、音视频转码等 - **批量操作**:批量上传、删除等 |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 2.0.0.11 | 104 | 1/14/2026 | |
| 2.0.0.10 | 150 | 12/12/2025 | |
| 2.0.0.9 | 440 | 11/18/2025 | |
| 2.0.0.8 | 225 | 10/27/2025 | |
| 2.0.0.7 | 142 | 10/25/2025 | |
| 2.0.0.6 | 149 | 10/25/2025 | |
| 2.0.0.5 | 145 | 10/25/2025 | |
| 2.0.0.4 | 138 | 10/25/2025 | |
| 2.0.0.3 | 150 | 10/25/2025 | |
| 2.0.0.2 | 178 | 10/24/2025 | |
| 2.0.0.1 | 210 | 10/23/2025 | |
| 2.0.0 | 211 | 10/23/2025 | |
| 1.0.1.25 | 209 | 10/19/2025 | |
| 1.0.1.24 | 211 | 10/19/2025 | |
| 1.0.1.23 | 292 | 10/19/2025 | |
| 1.0.1.22 | 294 | 10/19/2025 | |
| 1.0.1.21 | 298 | 10/19/2025 | |
| 1.0.1.20 | 300 | 10/19/2025 | |
| 1.0.1.19 | 209 | 10/15/2025 | |
| 1.0.1.18 | 208 | 10/13/2025 |
更新基础库