You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于IDataCheck服务动态创建ASP.NET Core健康检查

问题背景

我有大量用于执行数据校验的服务,所有服务均实现了 IDataCheck 接口,该接口仅定义了一个 RunCheckAsync 方法。
此前在命令行应用中,我通过如下代码执行所有校验逻辑:

IEnumerable<IDataCheck>? services = _serviceProvider.GetServices<IDataCheck>();
foreach (IDataCheck? s in services)
{
    DataCheckResult result = await s.RunCheckAsync();
    // 处理校验结果,比如给出自动修复建议等
}

现在需要把这些数据校验逻辑接入ASP.NET Core健康检查体系,同时支持HealthCheck UI展示,需要满足两个核心要求:

  • 不需要为每个数据校验服务手动编写对应的健康检查实现,避免破坏IDataCheck的接口抽象价值
  • 不能把所有校验逻辑合并到单个健康检查中,避免UI端把所有校验结果堆在无格式的纯文本字段里,需要每个校验项在UI中独立、格式化展示

实现方案

你可以通过动态健康检查注册的方式同时满足两个诉求,无需为每个IDataCheck实现手动编写健康检查类,也能保证每个校验项在HealthCheck UI中独立展示。

1. 编写通用适配类

先实现一个通用的健康检查包装类,负责把任意IDataCheck实例的执行结果映射为健康检查返回格式:

public class DataCheckHealthCheck : IHealthCheck
{
    private readonly IDataCheck _dataCheck;
    private readonly string _checkDisplayName;

    // 构造函数传入具体的校验服务实例和展示名称
    public DataCheckHealthCheck(IDataCheck dataCheck, string checkDisplayName)
    {
        _dataCheck = dataCheck;
        _checkDisplayName = checkDisplayName;
    }

    public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        try
        {
            DataCheckResult checkResult = await _dataCheck.RunCheckAsync(cancellationToken);
            
            // 按你自己的DataCheckResult结构映射健康状态
            if (checkResult.Passed)
            {
                return HealthCheckResult.Healthy($"{_checkDisplayName} 校验通过", checkResult.Metadata);
            }

            return checkResult.IsWarning
                ? HealthCheckResult.Degraded($"{_checkDisplayName} 存在警告:{checkResult.Message}", data: checkResult.Metadata)
                : HealthCheckResult.Unhealthy($"{_checkDisplayName} 校验失败:{checkResult.Message}", data: checkResult.Metadata);
        }
        catch (Exception ex)
        {
            return HealthCheckResult.Unhealthy($"{_checkDisplayName} 执行异常", ex);
        }
    }
}

2. 实现自动注册扩展

编写扩展方法,启动时自动扫描所有注册到DI的IDataCheck实例,逐个注册为独立的健康检查条目,这里用启动过滤器避免提前构建服务提供者导致的生命周期异常:

public static class DataCheckHealthCheckExtensions
{
    public static IHealthChecksBuilder AddAllDataValidationChecks(this IHealthChecksBuilder builder)
    {
        builder.Services.AddSingleton<IStartupFilter>(sp => new DataCheckRegistrationStartupFilter());
        return builder;
    }

    private class DataCheckRegistrationStartupFilter : IStartupFilter
    {
        public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
        {
            return app =>
            {
                var healthChecksBuilder = app.ApplicationServices.GetRequiredService<IHealthChecksBuilder>();
                var allDataChecks = app.ApplicationServices.GetServices<IDataCheck>();

                foreach (var check in allDataChecks)
                {
                    // 用实现类名生成UI展示名,你可以替换成自己的命名规则
                    string displayName = check.GetType().Name
                        .Replace("DataCheck", string.Empty)
                        .Replace("Check", string.Empty);
                    
                    // 每个校验项注册为独立健康检查,打datacheck标签方便分组
                    healthChecksBuilder.AddCheck(
                        name: $"数据校验-{displayName}",
                        instance: new DataCheckHealthCheck(check, displayName),
                        failureStatus: HealthStatus.Unhealthy,
                        tags: new[] { "datacheck" }
                    );
                }

                next(app);
            };
        }
    }
}

3. 配置服务和中间件

在Program.cs中按顺序注册服务即可:

// 按原有方式注册所有IDataCheck实现,生命周期保持你之前的设置即可
builder.Services.AddTransient<IDataCheck, UserInfoDataCheck>();
builder.Services.AddTransient<IDataCheck, OrderConfigDataCheck>();
// 后续新增IDataCheck实现只需要在这里注册,会被自动纳入健康检查

// 注册健康检查,调用扩展自动注册所有数据校验项
builder.Services.AddHealthChecks()
    .AddAllDataValidationChecks();

// 配置中间件,开启UI响应格式支持
app.UseHealthChecks("/health", new HealthCheckOptions
{
    ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});

// 配置HealthCheck UI
app.UseHealthChecksUI(opt =>
{
    opt.UIPath = "/health-dashboard";
    // 其余UI配置按实际需求调整即可
});

注意:如果你的校验逻辑需要依赖Scoped生命周期的服务(比如数据库上下文),只需要把DataCheckHealthCheck的执行逻辑改为工厂模式,在健康检查执行时手动创建作用域解析对应IDataCheck实例即可,不需要修改核心适配逻辑。

方案优势

  • 零额外重复代码:新增IDataCheck实现时只需要按原有逻辑注册到DI,不需要编写任何健康检查相关代码,完全保留接口抽象的价值
  • 独立条目展示:每个数据校验项对应独立的健康检查节点,HealthCheck UI会单独展示每个校验项的状态、名称、详情,不会出现所有结果堆在纯文本块的问题
  • 灵活扩展:你可以根据需要给不同校验项配置独立的标签、失败状态、缓存时长,也可以在适配层自由映射校验结果到健康检查的结构化数据字段,支持UI展示自动修复建议等自定义内容

内容的提问来源于stack exchange,提问作者edelwater

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 17:43:13