Buffalo默认不支持c.JSON(),必须先禁用模板系统(app.Templates = nil),再通过return c.Render(200, r.JSON(data))显式返回JSON,否则会因查找json.html模板失败而报错。

Buffalo 默认不返回 JSON,必须显式禁用模板系统并调用 c.Render() 配合 r.JSON(),否则会尝试渲染 HTML 模板导致 404 或乱码响应。
为什么 c.JSON() 不存在,且直接写 c.Render(200, r.JSON(data)) 会失败
Buffalo 没有 c.JSON() 方法——这是常见误解,源于对 Gin/Echo 等框架的惯性记忆。你看到的“c.JSON()”要么是自定义中间件封装,要么是误抄代码。真正起效的是 c.Render(status, renderer),但前提是模板系统已关闭:
-
app.Templates = nil必须在app.go初始化后立即设置(例如app = buffalo.New(...)下一行),否则c.Render()内部仍会尝试加载 HTML 模板 - 若没关模板,
c.Render(200, r.JSON(data))会先找json.html模板,找不到就 fallback 到application/json.html,最终报template: "json.html" is undefined - 错误现象:API 接口返回空白、HTML 片段、或 404,而非预期 JSON
如何安全返回 JSON:两步不可省略
所有 API handler 必须同时满足以下两个条件,缺一不可:
- 在
app.go中禁用模板:app.Templates = nil - 在 handler 中显式调用:
return c.Render(200, r.JSON(data))(注意是return,不是裸调用) - 不要用
c.Render(200, r.HTML(...))或c.Render(200, r.String(...))混入 API 路由,它们会触发模板查找逻辑 - 若需返回 201(如 Create)、204(如 Delete),状态码必须传给
c.Render(),例如:c.Render(201, r.JSON(u))
按 Accept 头自动切换 JSON/XML/HTML 的写法
Buffalo 不自动解析 Accept,需手动判断。用 strings.Contains() 比 == 更鲁棒,能覆盖 application/json; charset=utf-8 这类带参数的值:
func UserHandler(c buffalo.Context) error {
accept := c.Request().Header.Get("Accept")
switch {
case strings.Contains(accept, "application/json"):
return c.Render(200, r.JSON(map[string]string{"name": "alice"}))
case strings.Contains(accept, "application/xml"):
return c.Render(200, r.XML(map[string]string{"name": "alice"}))
default:
return c.Error(406, errors.New("not acceptable"))
}
}
- XML 渲染要求结构体字段带
xml:"name"tag,否则输出空标签 - 纯 API 服务建议对非 JSON/XML 请求直接返回
406 Not Acceptable,而不是 fallback 到 HTML —— 后者容易暴露模板路径或引发 404 - 避免在
default分支用r.HTML(),除非你明确维护了对应模板
生成的 CRUD 代码怎么改造成 JSON API
buffalo generate resource users 生成的代码默认走 HTML 渲染,用于 API 时需批量修改:
- 删掉整个
templates/users/目录(或留作备份),否则c.Render()仍可能尝试加载 - 把
actions/users.go中所有c.Render(200, r.HTML(...))替换为c.Render(200, r.JSON(...));Create改为c.Render(201, r.JSON(...));Destroy改为c.Render(204, r.JSON(nil)) - 移除或注释掉
app.Use(csrf.Middleware)和app.Use(session.Middleware),否则 POST/PUT 请求会因缺少X-CSRF-Token或 cookie 而 400 - 检查模型结构体字段是否加了
json:"name"tag,否则 JSON 输出字段名全小写(Go 默认导出规则)
最易被忽略的是中间件清理——哪怕你把所有 c.Render() 都改对了,CSRF 中间件仍在拦截请求,前端发的 POST 会直接卡在 400,连 handler 都进不去。











