mengstack-website/guide/quality.md
MengStack Dev fca3326e34 docs: 官网文档全面更新 — M2-M8 指南 + API 参考 + v0.2.0 更新日志
- 更新 changelog.md: v0.2.0 记录 M2-M8 全部完成,路线图更新
- 新增 5 篇指南: RBAC、组织管理、审计日志、通知模块、质量横切
- 新增 5 篇 API 文档: RBAC(11)、Org(5)、Audit(1)、Settings(7)、Notification(7)
- 更新 api/overview.md: 完整 37 端点列表
- 更新 config.ts: 侧边栏分组 + 11 页 SEO 元数据
2026-10-03 01:55:55 +08:00

146 lines
3.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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`,确保时间字段按中国时区存储和返回。