HaSoLib 1.1.3

dotnet add package HaSoLib --version 1.1.3
                    
NuGet\Install-Package HaSoLib -Version 1.1.3
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="HaSoLib" Version="1.1.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="HaSoLib" Version="1.1.3" />
                    
Directory.Packages.props
<PackageReference Include="HaSoLib" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add HaSoLib --version 1.1.3
                    
#r "nuget: HaSoLib, 1.1.3"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package HaSoLib@1.1.3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=HaSoLib&version=1.1.3
                    
Install as a Cake Addin
#tool nuget:?package=HaSoLib&version=1.1.3
                    
Install as a Cake Tool

HaSoLib

HaSoLib 是一个面向 ASP.NET Core Web API 的基础设施集成包。它把项目启动配置、Autofac 自动注入、接口式 Minimal API 映射、统一返回、异常处理、输出缓存、Nacos 动态配置、MySQL、Redis、HTTP、gRPC、FTP 和定时任务封装成一套约定。

当前包版本:1.1.2

目标框架:net8.0net10.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();

调用顺序很重要:

  1. AddNacosService() 注册 Nacos 配置模型、基础设施配置回调和内置服务。
  2. AddServiceSetting() 注册 Controller、Swagger、Session、CORS、OutputCache、Autofac、AOP、HttpClient、gRPC 客户端等。
  3. AddAppSetting() 写入 Autofac 根容器,启用中间件并映射 Controller、接口式 API 和 SignalR Hub。
  4. 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

  • 正常返回会包装为 ResponseModelDataErrorTypeMsg
  • 集合返回会包装为 ResponseListModellistcount
  • 抛出 ShowException 会返回自定义错误码和消息。
  • 其他异常会返回 ErrorType = 101Msg = "系统错误",并记录异常信息。

可用属性:

[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 动态控制 IsUseRedisIsUseMemoryIsCompress

Nacos 动态配置

AddNacosService() 会:

  • 自动扫描入口程序集里实现 IConfigModel 的自定义配置模型。
  • 注册内置配置模型及回调:RedisKitConfigMysqlConfigCorsConfigAESConfigFtpLoginConfigAopConfigModelHttpClientConfigGrpcClientConfigDynamicConfigModelLogConfigModel
  • 根据 Debug_NacosConfigsRelease_NacosConfigsappsettings 读取 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 基于 linq2dbDataOptions 扩展方法:

var db = _MysqlKitService["mysql1"];
var list = await db.FetchWhereTake<MajorListEntity>(t => true, page: 1, PageLen: 20);

常用扩展包括:

  • FetchAll
  • FetchWhere
  • FetchWhereSelect
  • FetchWhereOrderBy
  • FetchWhereTake
  • FetchWhereCountInt
  • FetchWhereFirstOrDefault
  • InsertData
  • BulkCopyData
  • InsertOrUpdateData
  • UpdateData
  • DelData

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
  • 数据访问服务是否实现了 ISingletonDenpendencyIScopeDenpendencyITransitDenpendency
  • 聚合类是否直接继承 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 packGeneratePackageOnBuild 会把本 README 放入 NuGet 包。

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.3 0 6/25/2026
1.1.2 133 3/19/2026
1.1.1 109 3/19/2026
1.1.0 160 12/21/2025
1.0.8 159 12/21/2025
1.0.7 169 12/20/2025
1.0.6 260 12/15/2025
1.0.5 252 12/15/2025
1.0.4 245 12/15/2025
1.0.3 261 12/15/2025
1.0.2 252 12/15/2025
1.0.1 224 7/10/2025