【ASP.NET CORE】11.HQServer基础架构优化

前言

前面几篇文章已经把 JWT、Quartz.NET、RabbitMQ 等基础能力陆续接入到了 HQServer。功能能跑起来只是第一步,随着模块越来越多,如果继续把注册、管道、中间件、数据库初始化都堆在 Program.cs 里,后面维护会越来越吃力。

所以这一篇主要做一次工程化整理:不改变业务目标,不引入复杂框架,只把通用基础能力沉淀到公共层,让启动流程更清晰、异常输出更统一、RabbitMQ 消费更可靠,顺手把 Swagger 的 JWT 调试体验补齐。

一、这次优化解决什么问题

本次优化重点放在框架层和基础设施层,主要包括:

  • Program.cs 瘦身,只保留应用启动的主流程。
  • 基础能力统一通过扩展方法注册,避免启动类里堆大量细节。
  • 新增全局异常处理中间件,接口异常返回统一结构。
  • 新增统一响应模型 ApiResult,方便后续接口输出规范化。
  • 数据库启动预热增加重试机制,应用启动时更早暴露连接问题。
  • RabbitMQ 发布、消费、队列声明和异常处理进一步封装。
  • Swagger 增加 JWT Bearer 授权配置,调试受保护接口更方便。
  • 修正项目默认命名空间残留,保持项目命名一致。

敏感配置、测试性质的用户相关业务代码本篇不处理,后面会单独清理。

二、Program.cs 瘦身

优化前,很多项目都会把日志、Swagger、数据库、认证、SignalR、Quartz、RabbitMQ、管道配置全部写在 Program.cs。短期看直观,长期看会变成一个“大杂烩”。

优化后的启动入口只保留核心编排:

var builder = WebApplication.CreateBuilder(args);

builder.Host.UseCommonSerilog();
builder.Services.AddHQApplicationInfrastructure(builder.Configuration);
builder.Services.AddControllers();
builder.Services.AddHQSwagger();

var app = builder.Build();

await app.WarmupDatabaseAsync();
app.UseHQApplicationPipeline();

await app.RunAsync();

这样一眼就能看出应用启动顺序:创建宿主、注册基础设施、构建应用、预热数据库、挂载管道、运行服务。

三、统一基础设施注册

应用层新增 AddHQApplicationInfrastructure,把项目需要的基础能力集中注册:

public static IServiceCollection AddHQApplicationInfrastructure(
    this IServiceCollection services,
    IConfiguration configuration)
{
    services.AddAppSignalR();
    services.AddHQSqlSugar(configuration);
    services.AddHQServices(typeof(IServiceMarker).Assembly);
    services.AddHQJwtAuthentication(configuration);
    services.AddHQQuartz(configuration, typeof(Program).Assembly);
    services.AddHQRabbitMQ(options => configuration.GetSection("RabbitMQ").Bind(options));

    return services;
}

这种写法的好处是:以后新增底层能力时,只需要维护一个基础设施入口,不用反复污染启动类。

四、统一应用请求管道

中间件管道也被移动到了独立扩展方法中:

public static WebApplication UseHQApplicationPipeline(this WebApplication app)
{
    app.UseHQGlobalExceptionHandler();
    app.UseCommonSerilogRequestLogging();

    if (SerilogExtensions.IsDevelopmentEnvironment())
    {
        app.UseSwagger();
        app.UseSwaggerUI();
    }
    else if (SerilogExtensions.IsProductionEnvironment())
    {
        app.UseHsts();
    }

    app.UseAuthentication();
    app.UseAuthorization();
    app.MapControllers();
    app.MapAppHub();

    return app;
}

这里最重要的是顺序:全局异常处理放前面,认证授权放在控制器映射前面,开发环境才打开 Swagger。

五、全局异常处理和统一返回结构

框架层新增统一响应模型:

public sealed class ApiResult<T>
{
    public bool Success { get; init; }
    public string? Message { get; init; }
    public T? Data { get; init; }

    public static ApiResult<T> Ok(T? data, string? message = null)
        => new() { Success = true, Message = message, Data = data };

    public static ApiResult<T> Fail(string message, T? data = default)
        => new() { Success = false, Message = message, Data = data };
}

同时新增全局异常处理中间件。当接口出现未捕获异常时,日志记录详细错误,对外只返回统一的安全提示:

context.Response.StatusCode = StatusCodes.Status500InternalServerError;
context.Response.ContentType = "application/json; charset=utf-8";

var result = ApiResult.Fail("服务器内部错误,请稍后重试");
await context.Response.WriteAsync(JsonSerializer.Serialize(result, JsonOptions));

这样既避免把堆栈、路径、配置等内部信息暴露给前端,也方便前端按统一格式处理异常。

六、数据库启动预热

数据库连接问题如果等到第一个业务请求才暴露,排查体验并不好。这里新增启动预热:

public static async Task WarmupDatabaseAsync(this WebApplication app)
{
    using var scope = app.Services.CreateScope();
    var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>();

    const int maxRetries = 3;
    for (var retryCount = 0; retryCount < maxRetries; retryCount++)
    {
        try
        {
            await db.Ado.GetScalarAsync("SELECT 1");
            return;
        }
        catch when (retryCount < maxRetries - 1)
        {
            await Task.Delay(TimeSpan.FromSeconds(retryCount + 1));
        }
    }

    await db.Ado.GetScalarAsync("SELECT 1");
}

启动阶段会尝试执行一次轻量 SQL,并在短暂失败时重试。最终仍失败则让应用启动失败,方便尽早发现环境问题。

七、仓储事务策略微调

仓储层保留事务能力,但不再让所有查询默认都进入事务。常规查询默认直接执行,写操作仍使用事务保护:

private static readonly RepositoryExecutionOptions DefaultOptions =
    new(false, IsolationLevel.ReadCommitted);

public IBaseRepository<T, TKey> WithTransaction()
{
    return CreateExecutor(_options.EnableTransaction(IsolationLevel.ReadCommitted));
}

public IBaseRepository<T, TKey> WithoutTransaction()
{
    return CreateExecutor(_options.DisableTransaction());
}

这样可以减少不必要的事务开销,同时保留显式事务能力。对于需要一致性保护的业务,可以通过 WithTransaction() 主动开启。

八、RabbitMQ 消费可靠性增强

前一篇我们已经接入了 RabbitMQ,这次继续补强消费端行为:

  • 发布前统一创建通道并设置持久化消息属性。
  • 消费端使用手动确认机制。
  • 业务处理成功后执行 BasicAckAsync
  • 业务处理失败后执行 BasicNackAsync
  • 通过 RequeueOnConsumerError 控制失败消息是否重新入队。
  • 队列声明、交换机声明、绑定关系统一交给拓扑管理器处理。
try
{
    var json = Encoding.UTF8.GetString(args.Body.Span);
    var message = JsonSerializer.Deserialize<T>(json, JsonOptions);

    if (message is null)
        throw new InvalidOperationException("RabbitMQ 消息反序列化失败");

    await handler(message, cancellationToken);
    await channel.BasicAckAsync(args.DeliveryTag, false, cancellationToken);
}
catch (Exception ex)
{
    _logger.LogError(ex, "RabbitMQ 消息消费失败");
    await channel.BasicNackAsync(
        args.DeliveryTag,
        false,
        _options.RequeueOnConsumerError,
        cancellationToken);
}

生产环境里不建议失败消息无脑重新入队,否则可能因为一条坏消息造成无限消费失败。默认不重新入队会更稳,后续可以继续扩展死信队列。

九、Swagger 支持 JWT 调试

Swagger 增加 Bearer Token 配置后,可以直接在页面右上角授权,再调试需要认证的接口:

services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new OpenApiInfo
    {
        Title = "HQServer API",
        Version = "v1",
        Description = "HQServer 接口文档"
    });

    var securityScheme = new OpenApiSecurityScheme
    {
        Name = "Authorization",
        Description = "输入 Bearer Token,例如:Bearer {token}",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.Http,
        Scheme = JwtBearerDefaults.AuthenticationScheme,
        BearerFormat = "JWT"
    };

    options.AddSecurityDefinition(JwtBearerDefaults.AuthenticationScheme, securityScheme);
});

这一步不影响接口权限逻辑,只是提升开发调试体验。

十、构建与安全检查

本次优化完成后,执行项目构建:

dotnet build HQ.Application/HQ.Application.csproj --nologo

构建结果为 0 个错误。当前仍有两个用户测试代码相关警告,本篇先不处理,后续清理测试代码时统一删除。

同时执行包漏洞检查:

dotnet list HQ.Application/HQ.Application.csproj package --vulnerable --include-transitive

检查结果:当前源下没有易受攻击的包。

十一、总结

这次优化没有追求“炫技式重构”,而是围绕企业项目最容易踩坑的几个点做了稳妥整理:

  • 启动入口更短,项目结构更清晰。
  • 基础设施注册集中,后续扩展更容易。
  • 异常输出统一,避免泄露内部细节。
  • 数据库启动预热,环境问题更早暴露。
  • RabbitMQ 消费失败处理更可靠。
  • Swagger 调试 JWT 接口更顺手。

到这里,HQServer 的基础框架已经从“功能可用”进一步走向“结构更稳、维护更舒服”。后续可以继续补充死信队列、统一参数校验、接口审计日志、权限细粒度策略等企业项目常见能力。

© 版权声明
THE END
喜欢就支持一下吧
点赞11 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容