Revel中Filter与Interceptor职责严格分离:Filter控制全局请求流转,必须在app/init.go中注册且顺序执行;Interceptor仅作用于显式绑定的Action,支持细粒度拦截。

Revel 的中间件定制不是“加个函数就完事”,而是必须分清 Filter(过滤器)和 Interceptor(拦截器)的职责边界——前者控制整个请求管道的流转,后者只在 Action 前后介入。用错类型,轻则逻辑不触发,重则阻塞整个链路。
Filter 怎么插入到全局过滤器链?
Filter 是请求入口级的处理单元,每个请求都必须顺序经过所有注册的 Filter。它不能跳过、不能条件启用,只能通过修改 revel.Filters 全局变量来增删或重排。
- 必须在
app/init.go的init()函数中操作,否则无效(框架初始化后Filters就已锁定) - 自定义 Filter 函数签名必须严格为:
func(c *revel.Controller, filterChain []revel.Filter) - 手动调用
filterChain[0](c, filterChain[1:])才能继续执行后续过滤器;漏掉这句,请求就卡死了 - 如果想在某个 Filter 中提前终止流程(比如未登录跳转),需直接返回,**不要**调用后续 filterChain
示例:一个检查 API Token 的 Filter
func AuthFilter(c *revel.Controller, filterChain []revel.Filter) {
token := c.Params.Get("token")
if token == "" || !isValidToken(token) {
c.Result = c.RenderError("Unauthorized")
return // 终止链路,不调用 filterChain
}
filterChain[0](c, filterChain[1:]) // 继续
}
func init() {
revel.Filters = append(revel.Filters, AuthFilter) // 插入到末尾
// 或替换 RouterFilter:revel.Filters[1] = AuthFilter
}
Interceptor 怎么绑定到特定 Controller 或 Action?
Interceptor 不是全局生效的,它只对显式注册的目标生效,且仅在 Action 调用前后运行。它不参与路由、参数解析等前置流程,所以不能替代 Filter 做鉴权或 session 初始化。
- 方法拦截器必须是
func(c *YourController) xxx() revel.Result形式,且必须是导出方法(首字母大写) - 函数拦截器可复用,但签名必须是
func(*revel.Controller) revel.Result,不能带其他参数 -
revel.BEFORE阶段返回非nilrevel.Result会立即中断 Action 执行;revel.AFTER阶段返回结果会覆盖原Result,但不影响其他拦截器运行 - 多个拦截器按注册顺序执行,
revel.InterceptMethod和revel.InterceptFunc注册的会合并排序
示例:仅对 AdminController 的所有 Action 加权限校验
func (c AdminController) RequireAdmin() revel.Result {
if !c.connectedUser.IsAdmin() {
return c.Redirect(App.Index)
}
return nil
}
func init() {
revel.InterceptMethod(AdminController.RequireAdmin, revel.BEFORE)
}
Session 和 Flash 在 Filter/Interceptor 里怎么安全读写?
Session 和 Flash 数据在 Filter 链早期(SessionFilter、FlashFilter)才被加载,所以自定义 Filter 如果插在它们前面,c.Session 和 c.Flash 还是空的;Interceptor 则默认可用——但要注意,它们只是当前请求的快照,任何异步 goroutine 中的修改都无效。
- Filter 中读写 Session 必须确保位置在
SessionFilter之后,例如插入到ParamsFilter后面而不是开头 - Interceptor 中可直接用
c.Session["key"],但删除操作(delete(c.Session, "key"))只影响本次响应 Cookie,不会“真正删除”历史值 - 要实现逻辑过期(如验证码 60 秒失效),必须配合时间戳字段 + 每次读取时主动校验,不能依赖后台 goroutine 定时清理
-
c.Session.Save()必须显式调用,否则变更不会写入响应 Cookie
为什么我的自定义 Filter 没生效?常见断点排查
Filter 失效往往不是代码写错,而是生命周期或位置问题。最常踩的坑集中在三处:
- 在
app/controllers/*.go里定义 Filter 并试图在该文件init()中修改revel.Filters—— 错!revel.Filters必须在app/init.go中初始化,且该文件会被最先加载 - Filter 函数里忘了调用
filterChain[0](c, filterChain[1:]),导致后续所有 Filter 和 Action 都不执行,浏览器一直 pending - 把本该用 Interceptor 的逻辑(如记录某类 Action 日志)硬塞进 Filter,结果发现
c.Action是空字符串,因为 RouterFilter 还没运行,路由还没匹配 - 在 Filter 中 panic 但没包在
defer里,导致PanicFilter来不及捕获,整个服务崩溃
真正的请求生命周期管控,关键不在“加多少中间件”,而在于清楚每一层 Filter 和 Interceptor 的执行时机与数据可见性——RouterFilter 之前看不到 c.Action,SessionFilter 之前读不到 c.Session,Interceptor 的 FINALLY 阶段甚至收不到 panic 信息。这些边界一旦模糊,调试成本会指数上升。











