Documentation
¶
Overview ¶
Package middleware 提供框架自带的通用中间件(日志、错误恢复等)。 中间件与框架本体分离为独立包,避免循环依赖:middleware 依赖 sin, 使用时由应用侧自行挑选并挂载,如 s.AddMiddleware(middleware.Recovery())。
Index ¶
- Constants
- func BodyLimit(limit int64) sin.HandlerFunc
- func CORS(cfg CORSConfig) sin.HandlerFunc
- func Gzip() sin.HandlerFunc
- func Logger() sin.HandlerFunc
- func NotFound() sin.HandlerFunc
- func Recovery() sin.HandlerFunc
- func RequestID() sin.HandlerFunc
- func Timeout(d time.Duration) sin.HandlerFunc
- type CORSConfig
Constants ¶
const ( RequestIDKey = "sin/RequestID" // 键值表键,业务 handler 可用 c.Get(middleware.RequestIDKey) 取用 HeaderRequestID = "X-Request-Id" // HTTP 头,与上游网关约定保持一致 )
请求 ID 在请求级键值表中的键(见 Context.Set/Get)与传递 / 回写用的 HTTP 头。
Variables ¶
This section is empty.
Functions ¶
func BodyLimit ¶
func BodyLimit(limit int64) sin.HandlerFunc
BodyLimit 返回请求体大小限制中间件,防止超大请求耗尽内存 / 带宽:
- Content-Length 已知且超限:直接写出 413 并 Abort,业务 handler 不执行;
- 长度未知(chunked / 流式):以 http.MaxBytesReader 包裹请求体, 超过 limit 的读取返回错误,表单 / JSON 解析得到空值。
用法:BodyLimit(2 << 20) 限制请求体最多 2MiB。
func CORS ¶
func CORS(cfg CORSConfig) sin.HandlerFunc
CORS 返回跨域资源共享(CORS)中间件:
- 请求带 Origin 且在放行范围内时,写出 Access-Control-Allow-* 响应头;
- 预检请求(OPTIONS + Origin)直接应答 204 并 Abort,不进入业务 handler (未匹配路由也没关系:中间件链在路由匹配前收集、匹配后执行,照样生效);
- Origin 不在白名单时不写跨域头(浏览器会拦截响应),请求本身按普通流程继续。
放行策略:AllowOrigins 含 "*" 时回写 "*";否则要求 Origin 与列表完全一致, 原样回写该 Origin 并附 Vary: Origin,避免中间缓存把 A 站的头发给 B 站。 注意:本实现不放开凭证(Cookie),需要 Allow-Credentials 时须与具体 Origin 配合,自行扩展。
func Gzip ¶
func Gzip() sin.HandlerFunc
Gzip 返回响应压缩中间件:客户端 Accept-Encoding 含 gzip、且响应尚未指定编码时, 经 gzip 流压缩响应体。惰性启用——handler 首次提交响应时才真正切换为压缩流; 链路结束后恢复原 writer,外层中间件后续写出的响应(如 NotFound 兜底的 404) 不经压缩、也不会被套上 Content-Encoding。 注意:与 Timeout 组合时放谁在外层都可以(Gzip 在外则超时的 504 也被压缩,客户端 带 Accept-Encoding 时可正常解压;在内则 504 不压缩)。
func Logger ¶
func Logger() sin.HandlerFunc
Logger 返回日志中间件:整条处理链执行完毕后,记录本次请求的状态码、URI 与耗时。 依赖 Context.Next 的语义——c.Next() 返回时后续链路已全部执行完, 此时状态码已由业务 handler 写入 c.StatusCode,可如实记录。 若链路内注册了 RequestID 中间件(须在 Logger 之内),日志自动带上请求 ID 便于关联。
func NotFound ¶
func NotFound() sin.HandlerFunc
NotFound 返回 404 兜底中间件:整条处理链执行完毕后,若仍无任何响应写出 (c.StatusCode 为 0——说明未匹配到路由、handler 为 nil,或业务 handler 自行选择不响应), 则写出标准 404。后置检查使其「只兜底、不抢占」:链中任何位置已写出的响应都原样保留, 例如鉴权中间件写出的 401、静态文件服务自己写出的 404 都不会被改写。
用法(与其他中间件组成标准栈,NotFound 放在 Recovery / Logger 之内):
s.AddMiddleware(middleware.Recovery(), middleware.Logger(), middleware.NotFound())
注意:不挂载本中间件时,未匹配路由的响应为空内容(状态码 200); 若 handler 发生 panic,会先被外层 Recovery 捕获写成 500,不会落入 404 分支 (panic 直接跳过了本中间件的后续检查)。
func Recovery ¶
func Recovery() sin.HandlerFunc
Recovery 返回错误恢复中间件:捕获后续 handler 链中的 panic,记录错误与调用栈后 向客户端返回 500,避免单个请求的异常导致连接被服务端粗暴断开。
原理:先调用 c.Next() 执行后续链路,再用 defer recover 兜住链路中的 panic; 若不挂载本中间件,业务 handler 的 panic 会一路抛到 net/http(连接被关闭)。 响应统一为不含内部细节的 "Internal Server Error",避免向客户端泄露实现信息。 若 panic 前响应已部分写出(c.StatusCode 非 0),则保留已写内容、仅记录日志—— 此时再补写 500 只会产生 superfluous WriteHeader 并把错误文本拼进半截响应。 写出 500 时同步记录 c.StatusCode,供外层 NotFound / Logger 正确判断。
func RequestID ¶
func RequestID() sin.HandlerFunc
RequestID 返回请求 ID 中间件:优先透传上游(网关 / 负载均衡)写入的 X-Request-Id, 没有或**不合法**(见 validRequestID)时生成 16 位随机十六进制串;随后写入请求级 键值表(键 RequestIDKey)并回写响应头。与 Logger 配合时须注册在 Logger 之内 (标准栈:Recovery, Logger, RequestID, NotFound),日志会自动带上请求 ID 便于关联。 上游 ID 是不可信输入:不校验就透传会把超长 / 含控制字符的值引入日志与响应头, 构成日志注入与响应头注入面。
func Timeout ¶
func Timeout(d time.Duration) sin.HandlerFunc
Timeout 返回限时执行中间件:把 Timeout 之后的处理链放入独立 goroutine 执行, 最多等待 d——限时内完成则响应原样生效;超时则写出 504,此后内层链路的一切写入 (业务响应、NotFound 兜底等)经 guardWriter 全部丢弃,避免与超时响应并发写同一 ResponseWriter(数据竞争)。
三个必须知道的取舍(Go 无法强杀 goroutine、panic 无法跨 goroutine 传播所致):
- 超时的链路仍会继续执行到自然结束(短时间占用资源),本中间件只保证其输出 不生效;业务若支持取消应自行监听信号;
- 超时路径上,外层 Context.Next 循环与内层 goroutine 会并发读写 c.index / c.StatusCode 等字段(无锁的 int 读写,实际危害是 -race 报告而非逻辑错误), 标准栈建议把 Timeout 挂在靠内层的位置,让外层尽量少在超时后触碰 Context;
- 内层 panic 经带缓冲 channel 转回本 goroutine 重新抛出,由外层 Recovery 统一兜底成 500——因此 Timeout 须注册在 Recovery 之内。
超时前若响应已部分写出(状态码非 0),保留已写内容、仅记录日志; 超时写出 504 时同步记录 c.StatusCode,供外层 NotFound(跳过 404 兜底)与 Logger(记录正确状态码)判断——约定同静态文件的直写规则。
Types ¶
type CORSConfig ¶
type CORSConfig struct {
AllowOrigins []string // 允许的源(scheme://host[:port]),"*" 表示放行所有源
AllowMethods string // 允许的方法列表,空则用默认值
AllowHeaders string // 允许的请求头列表,空则用默认值
}
CORSConfig 是 CORS 中间件配置。