Buffalo路径参数必须用{name}语法注册,如/posts/{id},不支持: id等其他格式;c.Param()仅读路径段值,查询参数和表单数据需分别用URL.Query()或c.Bind()处理,三者严格隔离。

路径参数必须用 {name} 语法注册,不能靠正则或自定义分隔符
Buffalo 的路由参数只支持 {name} 占位符形式,比如 /posts/{id} 或 /posts/{post_id}。它不识别 :id(Express 风格)、(Starlette 风格),也不接受你在路径里写 /posts/:id/edit——这种写法注册后能启动,但 c.Param("id") 永远返回空字符串。
正确做法是:在 config/routes.go 中显式写出带花括号的路径:
app.GET("/posts/{id}", PostsShow)
app.Scope("/posts/{post_id}", func(app buffalo.App) {
app.GET("/comments", CommentsIndex)
})
-
{id}和{post_id}是两个独立参数名,大小写敏感,后续必须用完全一致的字符串调c.Param("id")或c.Param("post_id") - 同一个路由里不能重复使用相同参数名,如
/posts/{id}/comments/{id}是非法的,编译期不会报错,但运行时第二个{id}会被忽略 - 参数值默认是字符串,即使你期望它是数字,也得自己转:
id, _ := strconv.Atoi(c.Param("id")),别指望框架自动做类型转换
c.Param() 只读路径参数,查询参数和表单数据得另处理
c.Param("id") 只从 URL 路径段提取,比如请求 GET /posts/123?tab=comments,c.Param("id") 返回 "123",而 tab=comments 属于查询参数,得用 c.Request().URL.Query().Get("tab") 或更安全的 c.Param("tab") 不起作用。
常见错误现象:
- 写了
/users/{id}?q=xxx路由,以为c.Param("q")能拿到xxx→ 实际返回空 - POST 表单提交到
/users/{id},想用c.Param("email")取邮箱字段 → 完全无效,表单字段要走c.Bind() - 混用
c.Param()和c.Request().FormValue()却没区分来源,导致参数覆盖或遗漏
记住:路径参数、查询参数、表单数据三者隔离,Buffalo 不做自动合并。
嵌套路由参数需手动透传,Resource 不会自动继承
用 app.Scope("/posts/{post_id}", ...) 注册嵌套路由后,{post_id} 不会自动注入到子资源 handler 的结构体字段里,也不会出现在 CommentsResource 的 Show 方法的 c 上下文以外的地方。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
你必须在每个 handler 里显式取值并用于业务逻辑:
func (v CommentsResource) Show(c buffalo.Context) error {
postID := c.Param("post_id")
if postID == "" {
return c.Error(400, errors.New("missing post_id"))
}
var comments []models.Comment
err := models.DB.Where("post_id = ?", postID).All(&comments)
if err != nil {
return err
}
c.Set("comments", comments)
return c.Render(200, r.HTML("comments/index.html"))
}
- 别依赖
CommentsResource自带的ID字段——它对应的是/comments/{id}里的{id},不是父级的{post_id} - 模板中生成嵌套链接时,不能直接用
commentsPath(),得改用postCommentsPath(map[string]interface{}{"post_id": post.ID}) - 如果 ORM 查询需要关联
Post,记得在models/comment.go加PostID uint字段,否则Where("post_id = ?")会查不到数据
参数校验容易漏掉空值和类型转换失败
c.Param() 返回的是原始字符串,哪怕路径是 /users/(末尾带斜杠但没值),c.Param("id") 也会返回空字符串 "",而不是 nil 或触发 panic。这导致很多 handler 在没检查就直接 strconv.Atoi("") 时崩溃。
安全写法要点:
- 先判空:
if idStr := c.Param("id"); idStr == "" { return c.Error(404, errors.New("id required")) } - 再转类型,并检查错误:
id, err := strconv.Atoi(idStr); if err != nil { return c.Error(400, err) } - 数据库查询前确认 ID 合法性,比如用
models.DB.Find(&u, id)前确保id > 0,避免查主键为 0 的脏数据 - 别在中间件里统一转参数——因为不同路由参数语义不同(
id是整数,slug是字符串),强转反而破坏灵活性
最常被忽略的一点:路径参数的合法性校验必须在业务逻辑之前完成,否则后续所有操作都可能基于一个无效值展开,debug 成本远高于前置拦截。










