HaSoLib 1.1.3
dotnet add package HaSoLib --version 1.1.3
NuGet\Install-Package HaSoLib -Version 1.1.3
<PackageReference Include="HaSoLib" Version="1.1.3" />
<PackageVersion Include="HaSoLib" Version="1.1.3" />
<PackageReference Include="HaSoLib" />
paket add HaSoLib --version 1.1.3
#r "nuget: HaSoLib, 1.1.3"
#:package HaSoLib@1.1.3
#addin nuget:?package=HaSoLib&version=1.1.3
#tool nuget:?package=HaSoLib&version=1.1.3
HaSoLib
HaSoLib 是一个面向 ASP.NET Core Web API 的基础设施集成包。它把项目启动配置、Autofac 自动注入、接口式 Minimal API 映射、统一返回、异常处理、输出缓存、Nacos 动态配置、MySQL、Redis、HTTP、gRPC、FTP 和定时任务封装成一套约定。
当前包版本:1.1.2
目标框架:net8.0、net10.0
示例项目:HaSoLib.WebApi
快速开始
在业务 Web API 项目中引用包:
dotnet add package HaSoLib
或在本仓库示例中参考项目引用:
<ProjectReference Include="..\HaSoLib\HaSoLib.csproj" />
推荐的 Program.cs 启动顺序如下:
using HaSoLib.WebApiKit;
using HaSoLib.WebApiFastKit.Initialize;
using HaSoLib.WebApiKit.RequestKit.DynamicConfig;
var builder = WebApplication.CreateBuilder(args);
await builder.AddNacosService();
builder.AddServiceSetting();
var app = builder.Build();
app.AddAppSetting();
app.AddDynamicConfigApi();
app.SetServiceDI();
app.Run();
调用顺序很重要:
AddNacosService()注册 Nacos 配置模型、基础设施配置回调和内置服务。AddServiceSetting()注册 Controller、Swagger、Session、CORS、OutputCache、Autofac、AOP、HttpClient、gRPC 客户端等。AddAppSetting()写入 Autofac 根容器,启用中间件并映射 Controller、接口式 API 和 SignalR Hub。SetServiceDI()给ServiceBase及其直接子类中的静态属性注入容器服务,并执行IAopPreheat.PreheatAsync()。
核心继承逻辑
HaSoLib 的主要约定是:接口负责 API 契约,类负责业务实现,标记接口负责生命周期,ServiceBase 负责聚合基础设施。
Program.cs
-> AddNacosService()
-> AddServiceSetting()
-> Autofac + AOP + automatic class scanning
-> AddAppSetting()
-> AddRequestMaps()
-> SetServiceDI()
-> inject static properties on ServiceBase and its direct child classes
IService
-> I{ServiceName}
-> route and method attributes
ServiceBase
-> ServiceAggregation
-> static shortcuts for MySQL, Redis, FTP, HttpClient, gRPC and custom services
-> {ServiceName} : ServiceAggregation, I{ServiceName}
示例项目中的聚合层:
using HaSoLib.WebApiFastKit;
using HaSoLib.MySqlKit;
using StackExchange.Redis;
public class ServiceAggregation : ServiceBase
{
public static DataOptions MysqlCyqfDbzyData => _MysqlKitService["mysql1"];
public static IDatabase RedisDB => _RedisKitService["redis1"];
public static SqlAccess sqlAccess { get; set; }
public static RedisAccess redisAccess { get; set; }
public static HttpAccess httpAccess { get; set; }
public static FtpServiceClient GrpcFtpServiceClient { get; set; }
}
业务接口和实现类:
using HaSoLib.WebApiKit.RequestKit;
using HaSoLib.WebApiKit.RequestKit.OutputCache;
[MapRoute("/FetchDataService/v1", "测试接口分组1")]
public interface IFetchDataService : IService
{
[OpCache(OutputCacheEnum.Expire10)]
Task<TestConfigModel> GetTestConfigModel(HttpContext httpContext);
[HttpGet]
Task<List<MajorListEntity>> GetMajorList();
}
public class FetchDataService : ServiceAggregation, IFetchDataService
{
public Task<List<MajorListEntity>> GetMajorList()
{
return sqlAccess.FetchMajorList(1);
}
}
约束:
- API 接口必须继承
IService。 - 实现类和接口要按
ClassName/IClassName命名,例如FetchDataService/IFetchDataService。 - 自动扫描默认只扫描入口程序集
Assembly.GetEntryAssembly(),业务服务应放在启动项目中,或确保它们进入入口程序集扫描范围。 AddRequestMaps()只映射接口方法,不直接扫描实现类方法。
自动注入约定
ContainerBuilderExtend.AddAopClassBatchAutowired() 会扫描入口程序集中的类,并按标记接口注册到 Autofac:
| 标记接口 | 生命周期/用途 |
|---|---|
IService |
API 服务,单例注册,并由接口方法自动映射 Minimal API |
ISingletonDenpendency |
单例服务 |
IScopeDenpendency |
生命周期作用域服务 |
ITransitDenpendency |
瞬时服务 |
IHostedService |
后台服务 |
ITimedTaskService |
定时任务服务,Keyed 单例注册 |
IAopCacheCallRedis |
AOP 缓存使用的 Redis 适配接口 |
如果类存在同名接口 I{ClassName},注册时会优先绑定到该接口;否则注册类本身。
SetServiceDI() 会读取 ServiceBase 和直接继承 ServiceBase 的聚合类中的静态属性,按属性类型从 Autofac 取服务并写入。若取到的服务实现了 IAopPreheat,会立即执行预热方法。
路由映射
接口式 API 由 RequestKitServer.AddRequestMaps() 映射。
常用属性:
| 属性 | 作用 |
|---|---|
[MapRoute("/path", "tag")] |
设置接口分组路由和 Swagger tag,可重复添加多个版本路由 |
[AsActionName("name")] |
指定方法路由名称 |
[ApiTag("tag")] |
设置接口或方法 tag |
[HttpMethod(HttpMethodType.Get)] |
显式设置 HTTP 方法 |
[HttpGet] / [HttpPost] |
兼容 MVC 属性,用于显式设置 HTTP 方法 |
[OpCache(OutputCacheEnum.Expire10)] |
启用输出缓存策略 |
[NoOpCache] |
禁用继承自接口分组的输出缓存 |
路由占位符:
| 占位符 | 含义 |
|---|---|
[project] |
入口程序集名,去掉 _、-、空格和 . |
[controller] |
服务实现类名 |
[action] |
接口方法名或 [AsActionName] 指定值 |
没有 [MapRoute] 时,默认路由为:
/{ProjectName}/{ServiceClassName}/{ActionName}
没有显式 HTTP 方法时,HaSoLib 会根据参数和方法名推断:
[FromBody]、[FromForm]、文件上传或复杂对象参数优先映射为 POST。- 多个普通参数或基础类型单参数倾向 GET。
- 方法名以
Get开头倾向 GET,Post开头倾向 POST。 - 不确定时默认 POST。
统一返回和异常
接口式 API 默认经过 RequestEndpointFilter:
- 正常返回会包装为
ResponseModel:Data、ErrorType、Msg。 - 集合返回会包装为
ResponseListModel:list、count。 - 抛出
ShowException会返回自定义错误码和消息。 - 其他异常会返回
ErrorType = 101、Msg = "系统错误",并记录异常信息。
可用属性:
[IsUnify(false)] // 跳过统一返回包装
[IsListUnify(false)] // 列表不转换为 list/count
[IsStream(true)] // 流式响应,跳过统一包装
AOP 缓存
AddServiceSetting() 会启用 Autofac、Castle DynamicProxy 和 AOP 拦截器。
使用方式:
using HaSoLib.WebApiKit.AopKit;
[Aop]
public class SqlAccess : ServiceAggregation, ISingletonDenpendency, IAopPreheat
{
public virtual async Task<List<MajorListEntity>> FetchMajorList(int page)
{
return await MysqlCyqfDbzyData.FetchWhereTake<MajorListEntity>(t => true, page, 20);
}
public Task PreheatAsync() => Task.CompletedTask;
}
注意:
- Class interceptor 要求被拦截方法可被代理,建议使用
virtual async Task<T>。 - AOP 缓存只处理 async 且有返回值的方法。
[NoCache]可让单个方法跳过缓存。- 如果注册了
IAopCacheCallRedis,缓存可写入 Redis;否则只使用内存缓存或按配置关闭。 AopConfigModel可通过 Nacos 动态控制IsUseRedis、IsUseMemory、IsCompress。
Nacos 动态配置
AddNacosService() 会:
- 自动扫描入口程序集里实现
IConfigModel的自定义配置模型。 - 注册内置配置模型及回调:
RedisKitConfig、MysqlConfig、CorsConfig、AESConfig、FtpLoginConfig、AopConfigModel、HttpClientConfig、GrpcClientConfig、DynamicConfigModel、LogConfigModel。 - 根据
Debug_NacosConfigs或Release_NacosConfigs从appsettings读取 Nacos 监听配置。
appsettings.json 建议结构:
{
"Debug_NacosConfigs": [
{
"Listeners": [
{ "DataId": "ExampleConfig", "Group": "DEFAULT_GROUP" }
],
"Namespace": "your-namespace-id",
"ServerAddresses": [ "http://127.0.0.1:8848" ],
"UserName": "your-user",
"Password": "your-password",
"ConfigUseRpc": false
}
],
"Release_NacosConfigs": []
}
不要把真实账号、密码或公网配置中心地址提交到公共仓库。
自定义配置模型:
using HaSoLib.NacosKit;
public class TestConfigModel : IConfigModel
{
public int PageLen { get; set; }
public string? Name { get; set; }
}
var config = NacosKitCenter.GetConfig<TestConfigModel>();
配置映射不是只按 DataId 绑定。NacosKitCenter 会根据 JSON 属性和实现 IConfigModel 的类型做形状匹配,匹配成功后反序列化并执行对应回调。
常用配置模型示例
MySQL:
{
"MysqlConnections": [
{
"Name": "mysql1",
"ConnectionString": "Server=127.0.0.1;Port=3306;Database=demo;Uid=user;Pwd=password;"
}
]
}
Redis:
{
"RedisConnections": [
{
"Name": "redis1",
"User": "",
"Password": "password",
"Servers": [ "127.0.0.1:6379" ],
"DBIndex": 0
}
]
}
HTTP Client:
{
"HttpClients": [
{
"Name": "default",
"BaseAddress": "https://api.example.com",
"TimeOut": 30,
"Headers": {
"X-App": "demo"
}
}
]
}
gRPC Client:
{
"GrpcClient": [
{
"Name": "FtpServiceClient",
"BaseAddress": "https://grpc.example.com"
}
]
}
动态配置接口:
{
"DynamicConfig": {
"IsUse": true,
"Configs": [
{
"Key": "feature",
"Value": { "enabled": true }
}
]
}
}
启用后默认路径:
/{ProjectName}/DynamicConfig?Key=feature
/{ProjectName}/DynamicConfig?Key=feature&Child=enabled
数据访问
MySQL 基于 linq2db 的 DataOptions 扩展方法:
var db = _MysqlKitService["mysql1"];
var list = await db.FetchWhereTake<MajorListEntity>(t => true, page: 1, PageLen: 20);
常用扩展包括:
FetchAllFetchWhereFetchWhereSelectFetchWhereOrderByFetchWhereTakeFetchWhereCountIntFetchWhereFirstOrDefaultInsertDataBulkCopyDataInsertOrUpdateDataUpdateDataDelData
Redis:
var redis = _RedisKitService["redis1"];
await redis.StringSetAsync("key", "value");
HTTP:
var text = await httpClientFactory.GetStringAsync("https://www.example.com");
var data = await httpClientFactory.GetFromJsonAsync<MyDto>("/api/demo", "default");
FTP:
await _FtpKitService.UploadFile("local.txt", "/upload");
定时任务
实现 ITimedTaskService 并添加任务属性:
using HaSoLib.WebApiKit.RequestKit;
using HaSoLib.WebApiKit.TimedKit;
[TimedLoopTask(30, 0)]
public class CleanupTask : ITimedTaskService, ISingletonDenpendency
{
public void Main()
{
// do work
}
}
要求:
ITimedTaskService类必须添加[TimedLoopTask]或[TimedFixedTask]。TimedLoopTask(Interval, Count)中Count = 0表示无限循环。- 固定时间任务使用
[TimedFixedTask("08:00:00", "18:00:00")]。
常见接入检查
Program.cs是否按推荐顺序调用。- 业务服务是否有
I{ClassName}接口并继承IService。 - 数据访问服务是否实现了
ISingletonDenpendency、IScopeDenpendency或ITransitDenpendency。 - 聚合类是否直接继承
ServiceBase,并在SetServiceDI()之后再使用静态属性。 - AOP 方法是否为
virtual async Task<T>,需要跳过缓存的方法是否添加[NoCache]。 IAopCacheCallRedis是否有且只有一个主要实现。- Nacos 配置 JSON 是否能匹配目标配置模型的属性。
- 公开文档和仓库中是否没有真实 Nacos、MySQL、Redis、FTP 凭据。
包含到 NuGet 包
本项目已在 HaSoLib.csproj 中配置:
<PackageReadmeFile>README.md</PackageReadmeFile>
<None Include="README.md" Pack="true" PackagePath="\" />
后续 dotnet pack 或 GeneratePackageOnBuild 会把本 README 放入 NuGet 包。
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. 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
- Autofac (>= 8.0.0)
- Autofac.Extensions.DependencyInjection (>= 9.0.0)
- Autofac.Extras.DynamicProxy (>= 7.1.0)
- Castle.Core (>= 5.1.1)
- Castle.Core.AsyncInterceptor (>= 2.1.0)
- FluentFTP (>= 49.0.2)
- Grpc.AspNetCore (>= 2.67.0)
- linq2db (>= 6.0.0)
- linq2db.MySql (>= 6.0.0)
- Microsoft.Extensions.Http.Polly (>= 8.0.3)
- MySql.Data (>= 9.5.0)
- nacos-sdk-csharp (>= 1.3.5)
- nacos-sdk-csharp.AspNetCore (>= 1.3.5)
- Nito.AsyncEx (>= 5.1.2)
- NRedisStack (>= 0.12.0)
- NSwag.AspNetCore (>= 14.6.0)
- Polly (>= 8.3.1)
- Polly.Contrib.WaitAndRetry (>= 1.1.1)
- Polly.Extensions.Http (>= 3.0.0)
- Serialize.Linq (>= 4.0.167)
- Swashbuckle.AspNetCore.Annotations (>= 10.0.1)
- Swashbuckle.AspNetCore.SwaggerGen (>= 10.0.1)
- Swashbuckle.AspNetCore.SwaggerUI (>= 10.0.1)
- System.Runtime.Caching (>= 8.0.0)
-
net8.0
- Autofac (>= 8.0.0)
- Autofac.Extensions.DependencyInjection (>= 9.0.0)
- Autofac.Extras.DynamicProxy (>= 7.1.0)
- Castle.Core (>= 5.1.1)
- Castle.Core.AsyncInterceptor (>= 2.1.0)
- FluentFTP (>= 49.0.2)
- Grpc.AspNetCore (>= 2.67.0)
- linq2db (>= 6.0.0)
- linq2db.MySql (>= 6.0.0)
- Microsoft.Extensions.Http.Polly (>= 8.0.3)
- MySql.Data (>= 9.5.0)
- nacos-sdk-csharp (>= 1.3.5)
- nacos-sdk-csharp.AspNetCore (>= 1.3.5)
- Nito.AsyncEx (>= 5.1.2)
- NRedisStack (>= 0.12.0)
- NSwag.AspNetCore (>= 14.6.0)
- Polly (>= 8.3.1)
- Polly.Contrib.WaitAndRetry (>= 1.1.1)
- Polly.Extensions.Http (>= 3.0.0)
- Serialize.Linq (>= 4.0.167)
- Swashbuckle.AspNetCore.Annotations (>= 10.0.1)
- Swashbuckle.AspNetCore.SwaggerGen (>= 10.0.1)
- Swashbuckle.AspNetCore.SwaggerUI (>= 10.0.1)
- System.Runtime.Caching (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.