控制器必须嵌入 *revel.controller,接收者为值类型,方法返回 revel.result;否则导致 panic 或静默失效。

控制器必须嵌入 *revel.Controller
Revel 不接受裸 struct 作为控制器 —— 必须显式嵌入 *revel.Controller,否则框架无法注入请求上下文(如 c.Params、c.Session、c.Render)。常见错误是写成 type AppController struct { revel.Controller }(值嵌入)或漏掉指针符号,导致运行时 panic:“nil pointer dereference”。正确写法只有一种:type AppController struct { *revel.Controller }。
func(c AppController) 还是 func(c *AppController)?
Revel 要求控制器方法接收者是**值类型**(func(c AppController)),不是指针类型。这是因为框架在每次请求时创建新实例并传入副本;若用指针接收者,会破坏对象池复用逻辑,且可能导致 c.Render 等方法失效(内部依赖 receiver 的地址一致性)。实测中,指针接收者会导致模板渲染空白、Session 丢失、Flash 消息不显示等静默故障。
- ✅ 正确:
func(c AppController) Show() revel.Result - ❌ 错误:
func(c *AppController) Show() revel.Result - ⚠️ 注意:即使结构体里嵌入的是
*revel.Controller,接收者仍必须是值类型
方法签名必须返回 revel.Result 且首字母大写
Revel 通过反射扫描控制器中所有导出方法(首字母大写),筛选出符合签名的 action:参数可选(支持自动绑定 URL/query/form 数据),但返回值**必须且只能是 revel.Result**。任何其他返回类型(如 error、string 或多返回值)都会被忽略,表现为 404 或 panic “no matching action found”。
- 支持自动绑定的参数类型包括:
string、int、bool、time.Time,以及自定义 struct(需实现Bind方法) - 不要试图在 action 中直接写
http.ResponseWriter—— 这绕过 Revel 生命周期,导致 Session/Flash/Compress 等过滤器失效 - 返回
nil是非法的;必须显式调用c.Render()、c.Redirect()、c.RenderJSON()等构造revel.Result
避免在控制器里做耗时同步操作
Revel 默认每个请求独占一个 goroutine,action 方法内阻塞(如 HTTP 调用、DB 查询未加 context timeout)会直接拖垮吞吐。虽然可以手动启 goroutine + channel,但更稳妥的做法是:把耗时逻辑拆进 service 层,并在 action 中用带超时的 context.WithTimeout 控制;或者启用 Revel 的拦截器机制,在 Before 阶段预加载非关键数据。
- 别写:
logistics, _ := api.GetLogisticsInfo(...)(无超时、无 cancel) - 应写:
ctx, cancel := context.WithTimeout(c.Request.Context(), 2*time.Second); defer cancel(); logistics, err := api.GetLogisticsInfoWithContext(ctx, ...) - 注意:
c.Request.Context()是请求生命周期绑定的 context,比新建context.Background()更安全











