Nop.WebApiFramework
1.0.2
dotnet add package Nop.WebApiFramework --version 1.0.2
NuGet\Install-Package Nop.WebApiFramework -Version 1.0.2
<PackageReference Include="Nop.WebApiFramework" Version="1.0.2" />
<PackageVersion Include="Nop.WebApiFramework" Version="1.0.2" />
<PackageReference Include="Nop.WebApiFramework" />
paket add Nop.WebApiFramework --version 1.0.2
#r "nuget: Nop.WebApiFramework, 1.0.2"
#:package Nop.WebApiFramework@1.0.2
#addin nuget:?package=Nop.WebApiFramework&version=1.0.2
#tool nuget:?package=Nop.WebApiFramework&version=1.0.2
Nop.WebApiFramework
ASP.NET Core 企业级 Web API 框架,开箱即用地集成了 DI、ORM、认证、日志、链路追踪、任务调度、对象存储、Swagger 文档等常用组件。
依赖:
Nop.Infrastructure+Microsoft.AspNetCore.App
快速开始 — Program.cs 模板
using Nop.WebApiFramework;
using Nop.WebApiFramework.ServiceExtentions;
using Nop.WebApiFramework.UserAccount;
var builder = WebApplication.CreateBuilder(args);
var model = builder.Configuration.Get<AppSettingsModel>()!;
// 1. 基础注入(HttpContext、内存缓存、HttpClient、CORS、OpenTelemetry、Serilog、Swagger)
builder.NetCoreBasic(model.ServiceName!, typeof(ApiGroupNames));
// 2. JWT 认证
builder.Services.AddJwtAuth(builder.Configuration);
builder.Services.AddScoped<UserAuthenticationFilter>();
// 3. 数据库(FreeSql + MySQL,也支持 SqlServer)
builder.Services.AddCustomMySql(model, b => {
b.UseConnectionString(FreeSql.DataType.MySql, model.FreeSqlDatabase.ConnectionString);
});
// 4. Hangfire 后台任务
builder.Services.AddCustomHangfirePkg(model);
// 5. 统一 ModelState 验证格式
builder.Services.AddControllers()
.AddNewtonsoftJson()
.ConfigureCustomApiBehaviorOptions();
var app = builder.Build();
// 6. 通用中间件管道(健康检查 → 请求日志 → 异常处理 → 链路追踪 → CORS)
app.UseWebApiFrameWork(model.ServiceName!);
// 7. Hangfire Dashboard
app.UseCustomHangfire();
// 8. Swagger
app.UseCustomSwagger(typeof(ApiGroupNames));
app.MapControllers();
app.Run();
核心架构
请求管道顺序
HTTP Request
→ NopHealthCheckMiddleware // /healthcheck 健康检查
→ HttpRequestBodyMiddleware // 记录请求体/响应体到 Serilog
→ GlobalExceptionMiddleware // 全局异常捕获 + 数据库连接池自愈
→ OpenTelemetryCustomTag // 注入 RequestId 到 Activity
→ CORS Middleware
→ Authentication (JWT)
→ Controller
统一响应格式
所有 Controller 继承 WebApiBaseController,有 8 个 Ok/Error 方法,响应统一为:
// 成功
{ "errCode": 0, "errMsg": "ok", "requestId": "abc123", "data": { ... } }
// 失败
{ "errCode": 500, "errMsg": "错误信息", "requestId": "abc123" }
响应模型类(位于 Nop.WebApiFramework):
| 类 | 字段 | 说明 |
|---|---|---|
JsonResponse |
ErrorCode, ErrorMessage, RequestId, IsError |
基础响应 |
JsonResponse<T> |
继承 JsonResponse + Data |
泛型响应 |
WebApiBaseController
public abstract class WebApiBaseController : ControllerBase
{
// 成功响应(无数据)
ActionResult Ok();
// 成功响应(带数据)
ActionResult Ok<T>(T data, int errCode = 0, string errMessage = "ok");
// 返回 JsonResponse<T> 对象(非 ActionResult,用于组装)
JsonResponse<T> Ok2<T>(T data, int errCode = 0, string errMessage = "ok");
// 错误响应
JsonResponse Error2(string errMessage, int errCode = 500);
ActionResult Error(string errMessage, int errCode = 500);
// 直接传入已有的 JsonResponse
ActionResult Ok(JsonResponse response);
ActionResult Ok<T>(JsonResponse<T> response) where T : class;
// 从请求头 UserId 获取当前用户 ID
long UserId { get; }
}
所有 IServiceCollection 扩展方法
以下方法可直接在 builder.Services. 或 builder. 上调用:
核心入口
| 方法 | 说明 | 注入/启用内容 |
|---|---|---|
builder.NetCoreBasic(serviceName, swaggerApiGroupType?) |
必调入口 | Kestrel 调优(10000并发)、HttpContextAccessor、MemoryCache、HttpClient、CORS、OpenTelemetry Zipkin、Serilog、Swagger |
app.UseWebApiFrameWork(appName) |
必调管道 | 健康检查、请求日志、异常处理、链路追踪Tag、CORS |
builder.AddCustomCors(policyName?) |
CORS 服务注册 | 允许所有来源,预检缓存1小时 |
app.UseCustomCors(policyName?) |
CORS 中间件 | 启用 CORS |
builder.ConfigureCustomApiBehaviorOptions() |
自定义 ModelState 验证格式 | 返回 JsonResponse 而非 .NET 原生 400 |
JWT 认证
| 方法 | 说明 |
|---|---|
services.AddJwtAuth(ConfigurationManager) |
从 JwtSetting 节点读取配置,注册 JWT Bearer 认证 |
数据库(FreeSql)
| 方法 | 说明 |
|---|---|
services.AddCustomMySql(AppSettingsModel, Action<FreeSqlBuilder>) |
注册 FreeSql 单例,含 SQL 监控、LINQ 参数化、自动同步表结构 |
后台任务(Hangfire)
| 方法 | 说明 |
|---|---|
services.AddCustomHangfirePkg(AppSettingsModel) |
注册 Hangfire(Redis 存储),禁止重试,失败自动删除 |
app.UseCustomHangfire() |
Hangfire Dashboard(/hangfire 路径,无认证限制) |
API 文档(Swagger)
| 方法 | 说明 |
|---|---|
services.AddCustomSwagger(Type enumApiGroupNames?) |
注册 SwaggerGen(含 Bearer Token 输入框、XML 注释、枚举分组) |
app.UseCustomSwagger(Type apiGroup?) |
启用 Swagger UI(折叠 Tag、隐藏 Models) |
配置模型 — AppSettingsModel
完整配置节点(appsettings.json 示例):
{
"ServiceName": "MyService",
"HttpProtocal": "http",
"TopDomainHost": "www.example.com",
"IdentityServerUrl": "https://idp.example.com",
"ConnectionStrings": {
"MySqlConn": "Server=localhost;Database=mydb;User=root;Password=xxx;",
"SqlServerConn": "",
"PostgresConn": "",
"RedisConnectString": "localhost:6379,password=xxx,defaultDatabase=0",
"Mongodbsetting": {
"Configuration": "mongodb://...",
"Database": "MyDb"
}
},
"FreeSqlDatabase": {
"ConnectionString": "Server=localhost;Database=mydb;User=root;Password=xxx;",
"MainDatabaseName": "mydb",
"SqlExecuteElapsedMillisecondsThreshold": 3000,
"UseMonitor": true,
"UseGenerateCommandParameterWithLambda": true,
"UseAdoConnectionPool": false
},
"JwtSetting": {
"SecurityKey": "your-256-bit-key-here-minimum-32-chars",
"Issuer": "Nop.WebApiFramework",
"Audience": "Nop.WebApiFramework",
"ExpireSeconds": 7200,
"RefreshExpiresSeconds": 604800
},
"ApiSettings": { "AK": "", "SK": "" },
"PrivateOSSConfig": {
"AccessId": "",
"AccessKey": "",
"EndPoint": "https://oss-cn-hangzhou.aliyuncs.com",
"BucketName": "private-bucket",
"Platform": "aliyun",
"CDNDomain": ""
},
"PublicOSSConfig": {
"AccessId": "",
"AccessKey": "",
"EndPoint": "https://oss-cn-hangzhou.aliyuncs.com",
"BucketName": "public-bucket",
"Platform": "aliyun",
"CDNDomain": "https://cdn.example.com"
},
"AIAgent": {
"Completions": { "ApiKey": "", "Uri": "" },
"Embeddings": { "ApiKey": "", "Uri": "" },
"GteRerank": { "ApiKey": "", "Uri": "" }
}
}
配置类说明
| 配置类 | 关键属性 | 说明 |
|---|---|---|
AppSettingsModel |
所有子配置的根 | 使用 builder.Configuration.Get<AppSettingsModel>() 获取 |
JwtSettingModel |
SecurityKey, Issuer, Audience, ExpireSeconds |
JWT 签发和验证参数,SecurityKey 至少 256 bit |
FreeSqlDatabaseSettings |
ConnectionString, MainDatabaseName, UseMonitor |
数据库配置,UseMonitor=true 时慢 SQL 告警 |
ConnectionStrings |
RedisConnectString, MySqlConn, SqlServerConn |
各类连接字符串 |
NopOSSEndPoint构造参数 |
accessId, accessKey, endPoint, bucketName, platform |
对象存储终端配置 |
JWT 认证体系
组件职责
| 组件 | 类型 | 职责 |
|---|---|---|
JwtExtentions.AddJwtAuth() |
静态扩展 | 注册 JWT Bearer 认证服务,配置验证参数 |
JwtTokenProvider |
可注入服务 | 签发 access_token + refresh_token |
AppJwtBearerEvents |
JwtBearerEvents 子类 | 从 Header 提取 Bearer token |
UserAuthenticationFilter |
IActionFilter | 从 Claims 提取用户信息 → 设置 LoginUserDto.Current |
LoginUserDto |
静态类 | AsyncLocal<UserDto> 存储当前请求的用户 |
UserDto |
DTO | Id, NickName, Role, PhoneNumber, Password, etc. |
使用方式
// 控制器中获取当前用户
public class MyController : WebApiBaseController
{
public IActionResult GetProfile()
{
// 方式1: 基础基类属性(仅返回 ID)
long userId = this.UserId;
// 方式2: 完整用户信息
var user = LoginUserDto.Current;
return Ok(user);
}
}
认证流程
客户端请求(Header: Authorization: Bearer xxx)
→ AppJwtBearerEvents.MessageReceived: 提取 token
→ JwtSecurityTokenHandler.ValidateToken: 验证签名/过期/Issuer/Audience
→ AppJwtBearerEvents.TokenValidated: 验证通过
→ UserAuthenticationFilter.OnActionExecuting: 从 Claims 提取 → LoginUserDto.SetCurrent()
→ Controller: 通过 LoginUserDto.Current 访问用户信息
对象存储 — OSS
兼容 S3 协议的 OSS 封装,通过 INopOSSService 接口使用。
平台常量(NopOSSEndPoint.Platform)
| 常量 | 平台 |
|---|---|
NopOSSEndPoint.AliyunPlantform |
阿里云 OSS("aliyun") |
NopOSSEndPoint.TencentPlantform |
腾讯云 COS("tencent") |
NopOSSEndPoint.CtyunPlantform |
天翼云("ctyun") |
NopOSSEndPoint.MinioPlantform |
MinIO 自建("minio") |
常用 API
// 构造终结点
var endpoint = new NopOSSEndPoint(
accessId: model.PublicOSSConfig.AccessId,
accessKey: model.PublicOSSConfig.AccessKey,
endPoint: model.PublicOSSConfig.EndPoint,
bucketName: model.PublicOSSConfig.BucketName,
platform: model.PublicOSSConfig.Platform,
cDNDomain: model.PublicOSSConfig.CDNDomain,
supportContentDisposition: true
);
// 创建服务实例
var oss = new NopOSSService(endpoint, logger);
// 上传
await oss.PutObject("path/file.jpg", contentString);
await oss.PutObject("path/file.jpg", stream, contentDisposition: "attachment; filename=\"file.jpg\"");
// 下载
var obj = await oss.GetObject("path/file.jpg");
// 分片上传(大文件)
var init = await oss.InitiateMultipartUpload("path/bigfile.zip");
var part1 = await oss.UploadPart("path/bigfile.zip", init.UploadId, stream1, 1, size);
await oss.CompleteMultipartUpload("path/bigfile.zip", init.UploadId, partETags);
// 获取访问 URL
string url = oss.GetObjectUrl("path/file.jpg"); // 公开访问
string signedUrl = oss.GetObjectUrl("path/file.jpg", withPreSigned: true); // 带临时 token
string token = oss.GetPresignedToken("path/file.jpg", expireMinutes: 30);
注册到 DI
// 私有 OSS
var privateEndpoint = new NopOSSEndPoint(/*...*/);
services.AddSingleton(privateEndpoint);
services.AddScoped<INopOSSService>(sp => new NopOSSService(
sp.GetRequiredService<PrivateNopOSSConfig>().ToEndpoint(), sp.GetRequiredService<ILogger<NopOSSService>>()
));
// 公开 OSS(同样方式)
异常处理
WebApiException
自定义业务异常,会被 GlobalExceptionMiddleware 统一捕获并返回 JsonResponse 格式。
// 简单抛出
throw new WebApiException("用户名已存在");
// 带错误码和 HTTP 状态码
throw new WebApiException(10001, "资源未找到", httpStatus: 404);
// 从已有 JsonResponse 创建
throw new WebApiException(JsonResponse.ErrorResponse("错误", requestId), httpStatus: 400);
GlobalExceptionMiddleware 特性
- 业务异常(
WebApiException):返回自定义 ErrorCode 和 ErrorMessage - ApplicationException:HTTP 400(BadRequest)
- KeyNotFoundException:HTTP 404(NotFound)
- 其他异常:HTTP 500,默认屏蔽异常详情(可通过
"SystemExtensionShield": false关闭) - 数据库连接恢复:检测 MySQL 连接失败 → 自动清空
MySqlConnection.ClearAllPools(),下次请求自动恢复 - 企业微信告警:设置
"EnableQyBotExceptionMessage": true可将异常推送到企业微信 Bot
中间件管道详情
| 中间件 | 注册方法 | 功能 |
|---|---|---|
NopHealthCheckMiddleware |
UseWebApiFrameWork |
/healthcheck 端点,返回服务名和心跳 |
HttpRequestBodyMiddleware |
UseWebApiFrameWork |
JSON 请求/响应体记录到 Serilog(跳过 SSE/流式响应,跳过文件流) |
GlobalExceptionMiddleware |
UseWebApiFrameWork |
全局异常捕获 + MySQL 连接池自愈 |
| OpenTelemetry Tag | UseWebApiFrameWork |
自定义 Activity Tag(RequestId 等) |
| CORS | UseWebApiFrameWork |
通过 UseCustomCors() |
Swagger 分组
通过枚举+特性实现 API 分组:
public enum ApiGroupNames
{
[GroupInfo("用户管理", "v1", "用户注册登录")]
User,
[GroupInfo("内容管理", "v1", "文章、评论等")]
Content,
[GroupInfo("系统管理", "v1", "配置、监控等")]
System
}
// Controller 上标注分组
[ApiGroup("User")]
[Route("[controller]")]
public class AccountController : WebApiBaseController { }
Type apiGroup 参数传入 typeof(ApiGroupNames) 即可。
FreeSql 数据库
services.AddCustomMySql(model, builder => {
builder.UseConnectionString(DataType.MySql, model.FreeSqlDatabase.ConnectionString);
// builder.UseConnectionString(DataType.SqlServer, model.FreeSqlDatabase.ConnectionString);
});
配置项说明(FreeSqlDatabaseSettings):
| 属性 | 默认值 | 说明 |
|---|---|---|
UseMonitor |
false |
启用时输出 SQL 调试日志 |
UseGenerateCommandParameterWithLambda |
true |
LINQ 参数化查询 |
UseAdoConnectionPool |
false |
ADO.NET 连接池 |
SqlExecuteElapsedMillisecondsThreshold |
3000 |
慢 SQL 告警阈值(毫秒) |
注册后使用:
public class UserService
{
private readonly IFreeSql _fsql;
public UserService(IFreeSql fsql) => _fsql = fsql;
public async Task<UserDto?> GetUser(long id) =>
await _fsql.Select<UserDto>().Where(u => u.Id == id).FirstAsync();
}
Hangfire 后台任务
// 注册(Redis 存储,Db=2,Prefix="Hangfire:")
services.AddCustomHangfirePkg(model);
// 启用 Dashboard(路径 /hangfire,无认证限制,生产环境需自行加鉴权)
app.UseCustomHangfire();
注意 model.ConnectionStrings.RedisConnectString 必须已配置。
OpenTelemetry & Serilog
NetCoreBasic 自动完成以下注册:
- OpenTelemetry: Zipkin Exporter + ASP.NET Core / HttpClient / SqlClient Instrumentation
- Serilog: 从
appsettings.json读取配置,自动 Filter 掉/healthcheck路径,注入Activity信息
Serilog 的 appsettings.json 示例:
{
"Serilog": {
"MinimumLevel": "Information",
"WriteTo": [
{ "Name": "Console" },
{
"Name": "Seq",
"Args": { "serverUrl": "http://localhost:5341" }
}
]
}
}
安装
dotnet add package Nop.WebApiFramework
依赖
本包依赖 Nop.Infrastructure,安装时会自动引入。
| 主要外部依赖 | 版本 | 用途 |
|---|---|---|
| Autofac.Extensions.DependencyInjection | 10.0.0 | DI 容器 |
| FreeSql | 3.5.303 | ORM |
| Hangfire.AspNetCore / Hangfire.Redis | 1.8.22 / 1.12.0 | 后台任务 |
| Serilog.AspNetCore / Serilog.Sinks.Seq | 9.0.0 | 日志 |
| OpenTelemetry 系列 | 1.14.0 | 链路追踪 |
| Swashbuckle.AspNetCore | 7.2.0 | API 文档 |
| Microsoft.AspNetCore.Authentication.JwtBearer | 10.0.0 | JWT 认证 |
| Newtonsoft.Json | 13.0.4 | JSON |
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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.Extensions.DependencyInjection (>= 10.0.0)
- FreeSql (>= 3.5.303)
- FreeSql.Extensions.JsonMap (>= 3.5.303)
- FreeSql.Provider.MySqlConnector (>= 3.5.303)
- FreeSql.Provider.SqlServer (>= 3.5.303)
- FreeSql.Repository (>= 3.5.303)
- Hangfire.AspNetCore (>= 1.8.22)
- Hangfire.Core (>= 1.8.22)
- Hangfire.Redis.StackExchange (>= 1.12.0)
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 10.0.0)
- Microsoft.Extensions.DiagnosticAdapter (>= 3.1.32)
- Microsoft.OpenApi (>= 1.6.22)
- Newtonsoft.Json (>= 13.0.4)
- Nop.Infrastructure (>= 1.0.2)
- OpenTelemetry.Exporter.Console (>= 1.14.0)
- OpenTelemetry.Exporter.Zipkin (>= 1.14.0)
- OpenTelemetry.Extensions.Hosting (>= 1.14.0)
- OpenTelemetry.Instrumentation.AspNetCore (>= 1.14.0)
- OpenTelemetry.Instrumentation.Http (>= 1.14.0)
- OpenTelemetry.Instrumentation.SqlClient (>= 1.0.0-rc9.10)
- Serilog.AspNetCore (>= 9.0.0)
- Serilog.Sinks.Seq (>= 9.0.0)
- Swashbuckle.AspNetCore (>= 7.2.0)
- Swashbuckle.AspNetCore.Newtonsoft (>= 7.2.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.