Buffalo框架不内置敏感词过滤,需用ahocorasick构建全局Trie实现高效匹配,应在Action层按业务字段过滤而非中间件全局扫描,并通过fsnotify+RWMutex支持热更新。

用 ahocorasick 构建高效匹配器,别碰 strings.Contains
Buffalo 的中间件或 action 层处理请求时,如果用 strings.Contains 或 strings.ReplaceAll 遍历敏感词列表,遇到几百个词、高并发请求,CPU 就会明显抖动,还容易漏跨字节匹配(比如“违\n法”被换行切开)。
实操建议:
- 用
github.com/BobuSumisu/ahocorasick初始化一个全局*ahocorasick.Trie,启动时从文件或 Redis 加载词库,避免每次请求重建 - 敏感词必须 UTF-8 编码存入,
trie.AddWord()前确认没有 BOM 或空行 - 匹配返回的是字节偏移(
Start/End),中文或 emoji 场景下,替换前先转[]rune再算位置,否则会切坏字符 - 不要在
buffalo.Context的Request.Body上直接io.ReadAll—— Buffalo 默认已读过一次,再读会返回空;改用c.Request().Body前先检查是否nil,或用buffalo.Request().Clone()(需 Go 1.22+)
在 Buffalo Action 中做输入过滤,不是在中间件里全局扫
Buffalo 的中间件(如 app.Use(func(c buffalo.Context) error {...}))适合鉴权、日志等通用逻辑,但敏感词过滤必须绑定具体业务语义:是过滤用户提交的 content 字段?还是只针对评论、签名、搜索关键词?全局扫描所有 POST body 容易误伤 JSON key、数字、base64 等非文本字段。
实操建议:
- 在对应 action(如
CreateComment)里显式取参数:content := c.Param("content"),再传给过滤函数 - 若需解析 JSON body,用
c.Bind()到结构体后,只对string类型字段调用过滤,避开int、bool、time.Time等非文本字段 - 拒绝策略要明确:发现敏感词是返回 400 错误?还是静默替换为
***?Buffalo 的c.Error()或c.Render()可直接响应,别用 panic
词库热更新难?用 fsnotify 监控文件 + sync.RWMutex 保护 trie
Buffalo 应用通常以二进制方式部署,改 sensitive-words.txt 后没法自动重载。硬重启服务影响可用性,而用 goroutine 定时轮询又浪费资源。
实操建议:
- 用
golang.org/x/exp/fsnotify(或更稳定的github.com/fsnotify/fsnotify)监听词库文件变更事件 - 新建一个带读写锁的包装结构:
type SensitiveTrie struct { mu sync.RWMutex; trie *ahocorasick.Trie } - 在
OnWrite回调中重建trie,然后mu.Lock()替换旧实例;所有过滤调用前先mu.RLock() - 首次加载失败时,
trie设为 nil 并记录 warn 日志,后续请求跳过过滤(避免阻塞),而不是 panic 或 crash

















