--- title: 质量横切 | 安全基线、限流、日志与优雅停机 - MengStack官方文档 --- # 质量横切 M8 为框架添加了生产级质量基线,覆盖安全、限流、日志、容错和测试基础设施。 ## 中间件链 所有请求经过以下中间件链(按顺序): ``` RequestID → RequestLogger → SecurityHeaders → CORS → RateLimit → BodyLimit → Recovery ``` ## IP 限流 基于内存的 IP 限流中间件,自动清理过期记录: ```go // 每个 IP 每分钟最多 100 次请求 r.Use(middleware.RateLimit(100, time.Minute)) ``` | 特性 | 说明 | |------|------| | 限流维度 | 客户端 IP | | 存储方式 | 内存 map + sync.Mutex | | 自动清理 | 后台 goroutine 每分钟清理过期记录 | | 超限响应 | HTTP 429 + `RATE_LIMITED` 错误码 | ::: warning 内存限流适合单机部署。多实例部署建议替换为 Redis 限流。 ::: ## 安全响应头 自动为所有响应添加安全头: | Header | 值 | 作用 | |--------|------|------| | `X-Content-Type-Options` | `nosniff` | 防止 MIME 类型嗅探 | | `X-Frame-Options` | `DENY` | 防止点击劫持 | | `X-XSS-Protection` | `1; mode=block` | 浏览器 XSS 过滤 | | `Referrer-Policy` | `strict-origin-when-cross-origin` | 控制 Referer 泄露 | | `Content-Security-Policy` | `default-src 'self'` | 限制资源加载来源 | ## 请求体限制 限制请求体最大为 10MB,防止大文件攻击: ```go r.Use(middleware.BodyLimit(10 << 20)) // 10 MB ``` - 先检查 `Content-Length` header,超限直接返回 413 - 再通过 `http.MaxBytesReader` 限制实际读取大小 ## 结构化请求日志 使用 Zap 替代 fmt,输出结构化 JSON 日志: ```json { "level": "info", "msg": "request", "status": 200, "method": "GET", "path": "/api/v1/profile", "query": "", "ip": "192.168.1.100", "user-agent": "Mozilla/5.0", "latency_ms": 12, "trace_id": "req-abc-123" } ``` 日志级别按状态码自动选择: | 状态码范围 | 日志级别 | |-----------|---------| | < 400 | INFO | | 400-499 | WARN | | >= 500 | ERROR | ## 自定义 Recovery 替代 `gin.Recovery()`,panic 信息通过 Zap 记录而非 stdout: ```go r.Use(middleware.Recovery(log)) ``` 记录的字段:panic 内容、stack trace、请求方法、路径、trace_id。 ## 优雅停机 应用收到终止信号后,等待最多 10 秒处理完进行中的请求: ```go // app.go OnStop: func(ctx context.Context) error { shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() return srv.Shutdown(shutdownCtx) } ``` ## 测试基础设施 ### testutil 包 ```go import "mengstack/internal/kernel/testutil" func TestSomething(t *testing.T) { log := testutil.TestLogger(t) // 测试专用 Zap logger cfg := testutil.TestConfig() // 最小化测试配置 } ``` ### 中间件测试 内置 11 个单元测试覆盖所有中间件: ```bash go test ./internal/app/middleware/ -v ``` | 测试 | 覆盖场景 | |------|----------| | TestRequestID_GeneratesNew | 无 trace_id 时自动生成 | | TestRequestID_PreservesExisting | 已有 trace_id 时保留 | | TestSecurityHeaders | 5 个安全头全部设置 | | TestRateLimit_AllowsWithinLimit | 限额内请求正常通过 | | TestRateLimit_BlocksOverLimit | 超限额返回 429 | | TestRequestLogger_DoesNotPanic | 日志中间件不 panic | | TestRecovery_HandlesPanic | 捕获 panic 并记录 | | TestBodyLimit_RejectsLargeBody | 超大 body 返回 413 | | TestBodyLimit_AllowsSmallBody | 正常 body 通过 | ## 数据库时区 PostgreSQL DSN 默认携带 `TimeZone=Asia/Shanghai`,确保时间字段按中国时区存储和返回。