敏感词过滤必须放在beego.BeforeExec阶段,此时RequestBody、QueryData等已就绪,可安全提取表单、JSON、URL参数;需按Content-Type分情况解析并遍历字符串字段;推荐使用成熟AC自动机库,全局初始化并支持热更新;过滤后应通过ctx.Input.SetData注入标记,由Controller自主决策响应,避免直接Abort导致逻辑中断。

敏感词过滤该放在哪个过滤点?
必须用 beego.BeforeExec,不能用 beego.BeforeRouter 或 beego.BeforeStatic。因为敏感词过滤要作用于用户提交的表单、JSON body、URL query 等动态内容,这些数据在 BeforeRouter 阶段尚未解析(ctx.Input.RequestBody 为空),而 BeforeStatic 只对静态文件路径生效,完全不触发。
正确时机是路由已匹配、控制器将执行但尚未调用方法前——即 BeforeExec。此时 ctx.Input.RequestBody 已读取,ctx.Input.QueryData() 和 ctx.Input.Data() 也可安全访问。
-
BeforeExec:可获取完整请求体、表单、JSON、Query 参数,适合做内容校验 -
BeforeRouter:仅能读ctx.Request.URL.Path和原始 header,无法解析 body -
BeforeStatic:只拦截/static/类路径,与业务内容无关
如何从请求中提取待检测文本?
Beego 不自动解析所有格式的请求体,需按 Content-Type 分情况处理。常见漏点是忽略 application/json 和 multipart/form-data 中的字段。
推荐统一提取逻辑:
- 若
ctx.Request.Method == "POST" || ctx.Request.Method == "PUT",先检查ctx.Request.Header.Get("Content-Type") -
application/json:用json.Unmarshal(ctx.Input.RequestBody, &data)解析后遍历字符串字段 -
application/x-www-form-urlencoded或multipart/form-data:用ctx.Input.QueryData()获取全部键值,再对 value 做过滤 - URL query(如 GET 请求):直接用
ctx.Input.Query("content") - 避免硬编码字段名,应遍历所有 string 类型字段(可用反射或结构体 tag 标记敏感字段)
AC自动机实现要不要自己写?
不建议手写 AC 自动机。Beego 项目通常体量中等,用现成的高性能库更稳,比如 github.com/wenlng/go-ahocorasick 或 github.com/BurntSushi/ahocorasick。自己实现容易在 fail 指针构建、内存泄漏、Unicode 支持上出错。
实操要点:
- 初始化时一次性构建 AC 树,不要每次请求都重建(
var ac *ahocorasick.AhoCorasick全局变量) - 敏感词加载支持热更新:监听文件变化,用
ac.Build()替换旧实例(注意加读写锁) - 匹配时用
ac.FindAllString(text),返回命中词列表;若只需判断是否违规,用len(matches) > 0即可,比逐个替换快 - 注意中文分词边界:AC 自动机默认按字节/符匹配,对“北京”“京市”等重叠词天然支持,无需额外切分
过滤后怎么响应才不破坏原有逻辑?
关键不是“拦住”,而是“透明介入”。直接 ctx.Abort() 或 ctx.Redirect() 会中断后续控制器执行,导致日志、统计、事务等逻辑丢失。
推荐做法是注入标记并让 Controller 自行决策:
- 在
BeforeExec过滤器中检测到敏感词后,设ctx.Input.SetData("sensitive_hit", true)和ctx.Input.SetData("sensitive_words", []string{"xxx"}) - Controller 方法内通过
v, ok := ctx.Input.GetData("sensitive_hit").(bool)判断,决定返回 400、脱敏后保存,还是打标入库 - 若必须拦截,用
ctx.ResponseWriter.WriteHeader(400)+ctx.ResponseWriter.Write([]byte(`{"error":"content rejected"}`)),再调ctx.Abort()终止链路 - 切勿在过滤器里调
ctx.Ctx.WriteString()后不Abort()—— 会导致重复写 response,HTTP 报错 “http: multiple response.WriteHeader calls”
最易被忽略的是:过滤器中修改了 ctx.Input.RequestBody(如脱敏后重写),但没同步更新 ctx.Request.Body,导致后续 Controller 的 this.ParseForm() 解析失败。真要改 body,得用 io.NopCloser(bytes.NewReader(newBytes)) 替换整个 ctx.Request.Body。


















