Revel请求必须严格经过完整过滤器链才能调用控制器:PanicFilter→RouterFilter(匹配路由,失败则404)→ParamsFilter(需手动ParseForm)→Session/FlashFilter→ActionInvoker(反射调用首字母大写、值接收者、返回revel.Result的方法)。

Revel 的请求必须走完整过滤器链才能进控制器
Revel 不允许跳过或重排过滤器,否则 RouterFilter 匹配失败、ActionInvoker 不调用控制器方法——现象是 404 或静默无响应。你不能手动 new Controller 并调用方法,也不能在中间件里提前 writeHeader。
关键链路顺序固定:PanicFilter → RouterFilter(查 conf/routes)→ ParamsFilter(填充 c.Params)→ SessionFilter / FlashFilter → ActionInvoker(反射调用)。
-
RouterFilter失败(如路径不匹配、routes文件语法错):直接返回 404,后续过滤器全跳过 -
ParamsFilter不解析 POST/PUT 的表单体,c.Params.Form始终为空,除非你显式调用c.Request.ParseForm() -
ActionInvoker只认首字母大写、接收值类型 receiver、返回revel.Result的方法;用func(c *App)会导致c.Render()空白、session 丢失
控制器结构体必须嵌入 *revel.Controller,且方法接收者是值类型
写成 type App struct { revel.Controller }(值嵌入)或 type App struct { *revel.Controller } 但方法用 func(c *App) 都会 panic 或静默失效。
正确写法只有一种:
<pre class="brush:php;toolbar:false;">type App struct {
*revel.Controller
}
func (c App) Index() revel.Result {
return c.Render()
}
- 嵌入必须是
*revel.Controller 指针,否则c.Session、c.Params等字段为 nil - 方法接收者必须是
App(值类型),不是*App;指针接收者会破坏 Revel 的实例复用机制 - 控制器文件必须放在
app/controllers/下,包名必须是app,否则反射找不到App.Index
路由配置错一个字符,控制器就收不到请求
conf/routes 是硬性契约,大小写、空格、括号、路径前缀全部敏感。写错不会报错,只会 404。
- HTTP 方法必须大写:
GET /login App.Login✅,get /login app.login❌ - 路径开头不能有空格:
GET /login(前面多空格)→ 解析失败 - 控制器名和方法名必须与 Go 代码完全一致:
App.Login对应func (c App) Login(),app.login或App.login都不匹配 - 带参数的路由如
GET /user/:id App.GetUser,要求方法签名含同名参数:func (c App) GetUser(id string);写成ID string或func (c App) GetUser()都是 404
参数绑定不是“自动全量”,要分场景选方式
c.Params 是统一入口,但怎么取决定行为。别指望 c.Params.Get("id") 能拿到 JSON body 里的 id,也别以为方法参数能自动接住 multipart 文件。
- URL 查询参数(
?q=go&limit=10)和路径参数(/user/:id)→ 用c.Params.Get("q")或方法参数func(c App) Search(q string, limit int) - POST 表单(
application/x-www-form-urlencoded)→ 必须先c.Request.ParseForm(),再读c.Request.Form或用方法参数绑定(仅限简单类型) - JSON 请求体 → 必须手动
c.Params.ParseBody(c.Request)或json.Unmarshal到结构体,c.Params里永远没有 JSON 字段 - 文件上传 →
c.Params.Files里有*multipart.FileHeader,但前提是已调用c.Request.ParseMultipartForm()
最易忽略的是:方法参数绑定对缺失字段只给零值,不报错也不提示;bool 类型把 "false" 和空字符串都当 false,无法区分“传了 false”和“根本没传”。











