敏感词过滤必须在请求体解析前拦截,使用aho-corasick算法预编译词库提升性能,并通过包装responsewriter实现响应体过滤;需重置request.body流、区分content-type处理、复用缓冲与编码器以降低gc压力。

直接在请求体解析前拦截并过滤,而不是等数据进模型或响应时再处理——否则敏感词可能已落库或暴露给前端。
中间件必须在 JSON 解析前读取原始 body
Echo 的 c.Request().Body 是单次可读流,一旦被 echo.Bind() 或手动 io.ReadAll() 消费过,再次读取会返回空。所以敏感词过滤中间件必须注册在所有依赖 body 解析的逻辑之前(比如 e.Use() 要早于路由注册,或至少早于使用 c.Bind() 的 handler)。
- 错误做法:在 handler 里先
c.Bind(&req)再对结构体字段做字符串扫描——此时原始 body 已不可逆消耗,且无法覆盖响应体中已含的敏感词 - 正确做法:中间件中用
io.ReadAll(c.Request.Body)读取原始字节,过滤后用io.NopCloser(bytes.NewBuffer(filtered))重置 body 流 - 注意:如果请求不是
application/json,比如是application/x-www-form-urlencoded,需先解码为 map 再序列化回 JSON 过滤,或单独适配 form 解析逻辑
用 Aho-Corasick 构建 Trie 树提升匹配性能
直接遍历敏感词列表调用 strings.Contains() 在词库超 500 条后延迟明显;Echo 高频接口下,每次请求都做 O(n×m) 扫描会拖垮吞吐。应预编译 AC 自动机,一次性匹配所有词。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
- 用
github.com/BobuSumisu/ahocorasick或github.com/gogf/gf/v2/container/gtree初始化词库,缓存在全局变量或依赖注入容器中 - 词库建议带版本号缓存,避免每次请求都查 DB;可用
sync.Once+time.AfterFunc实现热更新触发器 - 匹配时别只替换原始字符串——JSON 中的 key、number、bool 值不能动,必须先
json.Unmarshal()成map[string]interface{}或自定义结构体,再递归遍历所有string字段值
响应体过滤要包装 c.Response(),不能改写 handler 返回值
handler 返回的 c.JSON() 或 c.String() 会直接写入底层 http.ResponseWriter,此时再想过滤已来不及。必须用包装器劫持写入行为。
- 实现一个
filteringResponseWriter,嵌入echo.Response,重写Write()和WriteHeader() - 在中间件中用
c.Response().Writer = &filteringResponseWriter{...}替换原 writer - 仅对
Content-Type: application/json响应做反向 JSON 解析 + 字符串字段过滤,其他类型(如 HTML、纯文本)按需走正则或跳过 - 高频接口务必复用
bytes.Buffer和json.Decoder/Encoder,用sync.Pool管理,否则 GC 压力陡增
真正难的不是写个中间件,而是确保 body 读两次不丢、JSON 结构不塌、响应过滤不漏字段、词库更新不 reload 服务——这些边界条件比主逻辑更耗调试时间。










