根本原因是未安装必需NuGet包、未注册HealthChecksUI服务、未启用UseHealthChecksUI中间件,或配置顺序错误;ASP.NET Core默认不包含UI功能,必须手动集成AspNetCore.HealthChecks.UI及存储组件并正确配置管道。

Health Check UI 不是开箱即用的界面,必须显式安装包、注册服务、配置存储和暴露路由,否则访问 /health-ui 会直接 404。
为什么访问 /health-ui 总是 404?
根本原因不是路径写错,而是中间件没启用或服务未注册。ASP.NET Core 默认不带 UI 功能,它完全依赖第三方包和手动配置。
- 必须安装两个 NuGet 包:
AspNetCore.HealthChecks.UI和AspNetCore.HealthChecks.UI.InMemory.Storage(或数据库版) -
builder.Services.AddHealthChecksUI()必须在AddHealthChecks()之后调用,且不能漏掉AddInMemoryStorage() -
app.UseHealthChecksUI()必须放在app.UseRouting()之后、app.UseEndpoints()之前——顺序错会导致中间件不生效 - 别把
UseHealthChecksUI()和MapHealthChecks()混用:前者只负责 UI 渲染,后者才是执行检查的端点;UI 本身不执行检查,它轮询的是你配好的健康检查 API 地址
如何让 UI 正确显示数据库、Redis 等实际状态?
UI 只是“显示器”,真正决定状态的是你注册的健康检查项。如果 UI 显示全部 Healthy,大概率是你没加真实探测逻辑,或者连接字符串无效但没触发失败。
- 数据库检查必须用
.AddSqlServer("connstr"),而不是只注册DbContext类型——后者只校验 DI 是否注册成功,根本不连库 - Redis 检查要传有效
redisConnectionString,且确保服务可访问;本地测试时注意 Docker 容器网络隔离(如用host.docker.internal替代localhost) - 每个检查项默认超时 30 秒,但 UI 轮询间隔由
SetEvaluationTimeInSeconds(30)控制;若检查耗时接近或超过这个值,UI 会显示“上次检查时间很久以前”或空白 - 检查失败时,UI 默认只显示
Exception?.Message,敏感信息(如密码、堆栈)不会输出——这是安全设计,不是 bug
自定义响应 Writer 会影响 UI 吗?
不影响。UI 不解析你的 /health 响应体内容,它只关心 HTTP 状态码(200/503)和返回的 JSON 结构是否符合 HealthReport 格式。但如果你改了 ResponseWriter,务必保留字段名一致,否则 UI 解析失败会静默忽略该端点。
- UI 内部通过
HealthReport的Status、Entries、TotalDuration等属性渲染,不要删掉这些 key - 别在
ResponseWriter里序列化原始Exception对象——JSON 序列化会抛NotSupportedException,导致整个/health返回 500,UI 就收不到任何数据 - 若想在 UI 上看到更详细的失败原因,可在自定义 writer 中提取
kv.Value.Exception?.ToString()并塞进一个新字段(如failureDetail),但需同步修改 UI 的前端解析逻辑(不推荐,除非你自建 UI)
生产环境必须注意的三个细节
UI 在开发阶段很友好,但上线后容易因配置疏忽变成监控盲区。
- K8s 的
livenessProbe不该指向/health-ui——它返回的是 HTML,且无状态码语义;必须用/health端点做探针 - 内存存储(
AddInMemoryStorage)不适合多实例部署:每个 Pod 自己存自己的历史,UI 看到的只是单个实例的状态。集群场景必须换SqlServer或PostgreSql存储 - UI 路径(如
UIPath = "/health-ui")若与反向代理规则冲突(比如 Nginx 把所有/health*都转发给主服务),可能被意外暴露或拦截——建议用独立子域名或加身份验证中间件


















