Beego自定义中间件需显式注册且必须在beego.Run()前,函数签名固定为func(*context.Context),必须调用ctx.Next()才能继续流程;路径匹配为字面量前缀,终止请求应使用ctx.AbortWithStatusJSON()并及时return。

Beego 的自定义中间件(Filter)不是写完函数就自动生效的,必须显式注册、且注册时机和路径匹配方式稍有偏差就会完全不触发——这是最常被卡住的地方。
怎么写一个合法的 Filter 函数
Filter 本质就是一个 func(*context.Context) 类型函数,签名固定,不能多参数也不能少参数。它内部必须调用 ctx.Next() 才能让请求继续往下走;不调就等于“卡死”,后续所有 Filter 和 Controller 都不会执行。
常见错误包括:
- 误写成
func(http.ResponseWriter, *http.Request)—— Beego 不认这种 net/http 原生签名 - 忘记加
ctx.Next(),导致整个请求链中断却无报错,前端一直 pending - 在函数里直接 return 而不调
ctx.Next(),等同于没写
示例(日志过滤器):
func LogFilter(ctx *context.Context) {
log.Printf("→ %s %s", ctx.Request.Method, ctx.Request.URL.Path)
ctx.Next() // 必须有
log.Printf("← %s %s done", ctx.Request.Method, ctx.Request.URL.Path)
}
注册 Filter 必须在 beego.Run() 之前
Filter 注册语句必须出现在 beego.Run() 调用之前,通常放在 main.go 的 main() 函数开头,或 init() 中。放在 controller 或 router 初始化之后、甚至放在 beego.Run() 后面,都会导致注册失效——函数存在,但框架根本不会调用它。
正确位置示例:
func main() {
beego.InsertFilter("/api/", beego.BeeApp.FilterType, LogFilter) // ✅ 在 Run 前
beego.Run()
}
容易踩的坑:
- 把
InsertFilter()写在某个 controller 的Init()方法里 —— 此时 Beego 还没开始初始化路由系统,注册被忽略 - 用 bee run 启动时,
main()被多次执行(热重载),但 Filter 只注册一次,看似正常,实则可能漏掉某些路径 - 注册了多个 Filter 却没注意顺序:越早注册的越先执行,比如鉴权 Filter 应该比日志 Filter 更早注册,否则日志里可能记到未鉴权的请求
路径匹配不是正则,/api/ 和 /api 区别很大
Beego 的路径前缀匹配是字面量前缀比较,不支持正则表达式。写 "/api/" 表示只匹配以 /api/ 开头的路径(如 /api/users、/api/v2/login),而 "/api" 会错误匹配到 /apixxx 或 /api-token 这类路径。
常用匹配写法:
-
"/api/":推荐,安全覆盖所有 API 子路径 -
"/admin/:id":支持路由参数占位符,但仅限BeforeRouter阶段注册才有效 -
"/*":全局匹配,慎用,会影响静态资源等非业务路径
特别注意:OPTIONS 预检请求默认不进任何 Filter(截至 2026 年 11 月),如果要做 CORS 预检拦截,得单独在 BeforeRouter 阶段判断方法并手动处理,不能依赖路径匹配。
终止请求必须用 AbortWithStatusJSON(),不是 Abort()
想拦截非法请求并返回 JSON 错误?别用 ctx.Abort(401) —— 它只会中断流程,ResponseWriter 还是空的,前端收到的是空响应体 + 状态码,容易误判或超时。
应该用 ctx.AbortWithStatusJSON(),它内部已做三件事:设置状态码、设 Content-Type: application/json、序列化传入 map 并写入 body。
示例:
func AuthFilter(ctx *context.Context) {
auth := ctx.Input.Header("Authorization")
if auth == "" {
ctx.AbortWithStatusJSON(401, map[string]string{"error": "missing token"})
return
}
ctx.Next()
}
关键细节:
- 调用
AbortWithStatusJSON()后必须return,否则后续代码(比如ctx.Next())仍会执行 - 如果需要额外 Header(如
X-RateLimit-Remaining),得在AbortWithStatusJSON()之前用ctx.ResponseWriter.Header().Set()设置 - 不要在 Filter 里读
ctx.Request.Body多次——Beego 的ctx.Input.RequestBody已缓存,重复读会失败
最易忽略的一点:Filter 里改不了 Controller 的 this.Data,想透传数据给 Controller,请用 ctx.Input.SetData("key", value),然后在 Controller 里用 this.Input.GetData("key") 拿,别碰 ctx.Request.Context 或自定义字段。


















