162 lines
5.3 KiB
Markdown
162 lines
5.3 KiB
Markdown
|
|
---
|
|||
|
|
tags: []
|
|||
|
|
aliases:
|
|||
|
|
- "🛡️ 基础设施模块:错误处理与响应系统 (Infra: Error Handling & Response)"
|
|||
|
|
date created: 星期三, 十二月 10日 2025, 12:10:32 中午
|
|||
|
|
date modified: 星期三, 十二月 10日 2025, 12:12:02 中午
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 🛡️ 基础设施模块:错误处理与响应系统 (Infra: Error Handling & Response)
|
|||
|
|
|
|||
|
|
## 1\. 模块概述
|
|||
|
|
|
|||
|
|
本模块实现了 **Modular Clean Architecture** 中的基础设施层 (`Level 0` & `Level 1`),提供了一套统一的、安全的、可观测的 HTTP 响应机制。
|
|||
|
|
|
|||
|
|
**核心能力:**
|
|||
|
|
|
|||
|
|
- **统一契约:** 所有 API 响应(成功、失败、Panic、404)严格遵循 `{code, msg, data, trace_id}` 结构。
|
|||
|
|
- **安全降级:** 自动识别业务错误与系统错误。对系统级错误(如 SQL 失败)进行“掩码”处理,防止敏感信息泄露。
|
|||
|
|
- **可观测性:** 集成 Prometheus 埋点,通过 `X-Biz-Code` 实现业务级监控;全链路 TraceID 自动注入。
|
|||
|
|
- **开发体验:** 提供 `Responder` 接口与工厂模式,支持 Handler 层的依赖注入与 Mock 测试。
|
|||
|
|
|
|||
|
|
-----
|
|||
|
|
|
|||
|
|
## 2\. 文件清单 (File Manifest)
|
|||
|
|
|
|||
|
|
以下代码位于项目根目录 `gitea-aliyun/Klein/enterprise-cms-core/` 下:
|
|||
|
|
|
|||
|
|
### Level 0: 基础领域层 (`internal/pkg/ecode`)
|
|||
|
|
|
|||
|
|
> **依赖:** 零依赖 (仅标准库)
|
|||
|
|
|
|||
|
|
| 文件名 | 类型 | 核心职责 |
|
|||
|
|
| :--- | :--- | :--- |
|
|||
|
|
| `code.go` | Const | **错误码注册表**。定义 `1xxxx` (系统) 和 `2xxxx` (业务) 常量。 |
|
|||
|
|
| `msg.go` | Data | **文案映射**。维护全局 `map[int]string`,提供并发安全的 `GetMsg`。 |
|
|||
|
|
| `error.go` | Struct | **错误实体**。实现 `error` 接口,支持 `WithMsg`/`WithDetails` 扩展。 |
|
|||
|
|
| `ecode_test.go` | Test | 验证并发安全性及不可变性。 |
|
|||
|
|
|
|||
|
|
### Level 1: 应用工具层 (`internal/pkg/app`)
|
|||
|
|
|
|||
|
|
> **依赖:** `gin`, `ecode`
|
|||
|
|
|
|||
|
|
| 文件名 | 类型 | 核心职责 |
|
|||
|
|
| :--- | :--- | :--- |
|
|||
|
|
| `responder.go` | Interface | **接口定义**。定义 `Responder` 接口与 `Factory` 函数类型,用于解耦。 |
|
|||
|
|
| `response.go` | Impl | **核心实现**。封装 Gin Context,实现 JSON 序列化、错误清洗、监控埋点。 |
|
|||
|
|
| `options.go` | Pattern | **功能选项**。提供 `WithTraceID` 等扩展配置。 |
|
|||
|
|
| `response_test.go` | Test | 验证 JSON 契约、空指针防御及错误降级逻辑。 |
|
|||
|
|
|
|||
|
|
### Global: 全局中间件 (`internal/middleware`)
|
|||
|
|
|
|||
|
|
> **依赖:** `gin`, `pkg/app`, `pkg/ecode`, `prometheus`
|
|||
|
|
|
|||
|
|
| 文件名 | 类型 | 核心职责 |
|
|||
|
|
| :--- | :--- | :--- |
|
|||
|
|
| `recovery.go` | Safety | **Panic 兜底**。捕获 Panic 并转换为标准 JSON 500 响应。 |
|
|||
|
|
| `not_found.go` | Route | **404 兜底**。将无路由请求转换为标准 JSON 404 响应。 |
|
|||
|
|
| `metrics.go` | Monitor | **业务监控**。采集 `http_requests_total` 指标,包含 `biz_code` 标签。 |
|
|||
|
|
|
|||
|
|
-----
|
|||
|
|
|
|||
|
|
## 3\. 快速上手 (Quick Start)
|
|||
|
|
|
|||
|
|
### 3.1 定义新错误
|
|||
|
|
|
|||
|
|
在 `internal/pkg/ecode/code.go` 添加常量,并在 `msg.go` 添加文案。
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// code.go
|
|||
|
|
const UserBalanceInsufficient = 20005
|
|||
|
|
|
|||
|
|
// msg.go
|
|||
|
|
msg = map[int]string{
|
|||
|
|
// ...
|
|||
|
|
UserBalanceInsufficient: "User Balance Insufficient",
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.2 在 Handler 中使用 (推荐写法)
|
|||
|
|
|
|||
|
|
使用依赖注入的 `app.Factory` 创建响应器,而非直接调用 `app.New`。
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
import (
|
|||
|
|
"github.com/gin-gonic/gin"
|
|||
|
|
"gitea-aliyun/Klein/enterprise-cms-core/internal/pkg/app"
|
|||
|
|
"gitea-aliyun/Klein/enterprise-cms-core/internal/pkg/ecode"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
type UserHandler struct {
|
|||
|
|
// 注入 Responder 工厂,便于测试 Mock
|
|||
|
|
RespFactory app.Factory
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (h *UserHandler) Create(c *gin.Context) {
|
|||
|
|
// 1. 创建响应器
|
|||
|
|
resp := h.RespFactory(c)
|
|||
|
|
|
|||
|
|
// 2. 模拟业务逻辑
|
|||
|
|
if err := h.Service.Create(); err != nil {
|
|||
|
|
// 自动处理错误:如果是业务错误直接返回;如果是系统错误则降级并记录日志
|
|||
|
|
resp.Error(err)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 3. 成功响应
|
|||
|
|
resp.Success(gin.H{"status": "created"})
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.3 系统接入 (Main.go)
|
|||
|
|
|
|||
|
|
在 HTTP Server 启动时注册全局中间件。
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
r := gin.New()
|
|||
|
|
|
|||
|
|
// 1. Recovery (必须最先注册)
|
|||
|
|
r.Use(middleware.Recovery())
|
|||
|
|
|
|||
|
|
// 2. Metrics (监控业务码)
|
|||
|
|
r.Use(middleware.BusinessMetrics())
|
|||
|
|
|
|||
|
|
// ... 注册业务路由 ...
|
|||
|
|
|
|||
|
|
// 3. 404 处理 (最后注册)
|
|||
|
|
r.NoRoute(middleware.NotFound())
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
-----
|
|||
|
|
|
|||
|
|
## 4\. 设计决策说明 (Architecture Decisions)
|
|||
|
|
|
|||
|
|
### A. HTTP 200 Always 策略
|
|||
|
|
|
|||
|
|
- **规则:** 除非网络层崩溃,所有接口(包括业务错误和系统错误)均返回 `HTTP 200 OK`。
|
|||
|
|
- **原因:** 防止网关(Nginx/ALB)拦截非 200 响应并替换 Body,确保前端始终能解析 JSON 中的 `code`。
|
|||
|
|
|
|||
|
|
### B. 安全掩码 (Security Masking)
|
|||
|
|
|
|||
|
|
- **输入:** `db.Query` 失败返回 `sql: connection refused`。
|
|||
|
|
- **输出:** 前端收到 `{ "code": 10000, "msg": "Internal Server Error" }`。
|
|||
|
|
- **日志:** 服务端 Error Log 记录原始堆栈。
|
|||
|
|
- **目的:** 杜绝数据库结构、IP 等敏感信息通过报错接口泄露。
|
|||
|
|
|
|||
|
|
### C. 监控指标 (Metrics)
|
|||
|
|
|
|||
|
|
- **指标名:** `http_requests_total`
|
|||
|
|
- **关键标签:** `biz_code` (业务状态码)。
|
|||
|
|
- **SRE 告警:** 请针对 `biz_code >= 10000` (系统错误) 配置告警,而非 HTTP Status Code。
|
|||
|
|
|
|||
|
|
-----
|
|||
|
|
|
|||
|
|
## 5\. 测试指南
|
|||
|
|
|
|||
|
|
本模块已包含完整的单元测试与竞态检测 (Race Detection)。
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 运行所有测试
|
|||
|
|
go test -v -race ./internal/pkg/...
|
|||
|
|
```
|