revel过滤器需显式插入filters切片或通过filterconfiguringfilter动态配置;必须符合func(*controller, []filter)签名,手动调用filterchain[0](c, filterchain[1:])实现责任链,漏调则后续过滤器及actioninvoker不执行。

Revel 的过滤器不是插件式挂载,而是显式声明的函数链;你必须手动插入到 revel.Filters 切片中,或通过 FilterConfiguringFilter 在 action 级别动态增删——漏掉这一步,写得再对也不会执行。
定义符合 Filter 接口的函数
Revel 要求过滤器必须是 func(c *revel.Controller, filterChain []revel.Filter) 类型。它不是中间件“注册”模型,而是责任链递归调用模型:每个过滤器自己决定是否、何时、如何调用下一个。
常见错误是直接 return 或忘记调用 filterChain[0](c, filterChain[1:]),导致后续过滤器(包括 ActionInvoker)完全不执行,请求无声失败。
- 预处理逻辑写在调用前,后处理逻辑写在调用后
- 若要终止请求(如鉴权失败),直接赋值
c.Result = c.Unauthorized()并 return,**不要**再调用下一个 - 注意空链保护:
if len(filterChain) > 0 { filterChain[0](c, filterChain[1:]) } - 示例:
var AuthFilter = func(c *revel.Controller, fc []revel.Filter) { if c.Request.Header.Get("X-API-Key") != "secret" { c.Result = c.Unauthorized() return } if len(fc) > 0 { fc[0](c, fc[1:]) } }
全局插入到 revel.Filters 切片
Revel 在 app/init.go 的 init() 函数中初始化 revel.Filters。你不能 append 到它,而必须完整重赋值——因为框架启动后该切片就固定了。
顺序至关重要:RouterFilter 必须在你依赖路由信息(如 c.Params.Route)的过滤器之前;ParamsFilter 必须在你读取 c.Params.Int("id") 之前;否则会 panic 或读到空值。
- 想让 AuthFilter 在路由之后、参数解析之前运行?把它插在
RouterFilter后、ParamsFilter前 - 错误写法:
revel.Filters = append(revel.Filters, AuthFilter)—— 这会让 AuthFilter 在ActionInvoker之后执行,毫无意义 - 正确写法:
revel.Filters = []revel.Filter{ revel.PanicFilter, revel.RouterFilter, AuthFilter, // ← 插在这里 revel.ParamsFilter, revel.SessionFilter, revel.ActionInvoker, }
按 Controller 或 Action 动态配置过滤器
全局插入太粗粒度?用 FilterConfiguringFilter 提供的钩子,配合 revel.FilterAction() 或 revel.FilterController() 实现细粒度控制。
这个机制依赖 FilterConfiguringFilter 本身在过滤器链中——它默认就在 RouterFilter 之后、ParamsFilter 之前,所以能拿到已解析的路由信息。
- 只给某个 action 加日志:
revel.FilterAction(AppController.ShowUser).Add(LogFilter) - 给整个 controller 移除 session:
revel.FilterController(AppController{}).Remove(revel.SessionFilter) - 注意:这些调用必须放在
init()中、revel.Filters初始化之后,否则无效 - 移除操作有风险:比如移掉
ParamsFilter后,c.Params就是空的,所有参数读取都会失败
容易被忽略的上下文与生命周期问题
过滤器里没有 request context 的自动传递,c 是每次请求新建的,但它的字段(如 c.Params、c.Session)是共享的;你在过滤器里往 c 上塞自定义字段(如 c.User = user),后续过滤器和 action 都能访问——这是最常用的数据透传方式。
但别往 c 上存大对象或未关闭资源(如 open file、unclosed db tx),因为 c 生命周期仅限单次请求,且框架不负责清理你塞进去的东西。
- 不要在过滤器里启动 goroutine 并试图把结果写回
c——c可能已经返回响应,goroutine 写入会 panic - 需要异步处理?用 channel + 主 goroutine 等待,或改用拦截器(
revel.Interceptor)更安全 - 调试时加
revel.INFO.Printf("in AuthFilter for %s", c.Request.URL.Path),比 print 更可靠











