Files
Inbox/Go项目实战/03_基础设施/01_错误处理/03_README_错误处理.md

162 lines
5.3 KiB
Markdown
Raw Normal View History

2025-12-11 07:24:36 +08:00
---
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/...
```