Buffalo默认错误响应返回HTML,需手动实现JSON错误中间件统一处理404、500、panic等;核心是前置注册自定义中间件拦截echo.HTTPError和buffalo.RouteNotFoundError,并禁用默认ErrorHandler和Recovery。

Buffalo路由错误默认不走JSON,得手动接管
Buffalo 的 404、500 等 HTTP 错误默认渲染 HTML 模板(如 templates/errors/404.plush.html),不会自动转成 JSON。如果你的前端全是 AJAX 调用,返回 HTML 会直接被 JS 当作解析失败——这不是 bug,是设计如此。
要统一封装为 JSON,必须显式替换掉默认的错误中间件,不能只改模板。
- 别在
app.go里留着app.Use(middleware.ErrorHandler)默认配置 - 删掉或重命名
templates/errors/目录,否则某些 panic 场景仍可能 fallback 到 plush 渲染 - 确保所有自定义错误 handler 都调用
c.Render或c.JSON,而不是c.HTML
怎么写一个统一的 JSON 错误响应中间件
核心是拦截 echo.HTTPError 类错误(Buffalo 底层用 echo),并在 app.Use 中前置注册。它比路由匹配更早执行,能兜住未匹配路由和 panic。
示例中间件:
func JSONErrorHandler(next buffalo.Handler) buffalo.Handler {
return func(c buffalo.Context) error {
err := next(c)
if err == nil {
return nil
}
<pre class="brush:php;toolbar:false;"> status := http.StatusInternalServerError
msg := "Internal Server Error"
switch e := err.(type) {
case *echo.HTTPError:
status = e.Code
msg = e.Message.(string)
case buffalo.RouteNotFoundError:
status = http.StatusNotFound
msg = "Not Found"
}
c.Response().Header().Set("Content-Type", "application/json; charset=utf-8")
c.Response().WriteHeader(status)
json.NewEncoder(c.Response()).Encode(map[string]string{
"error": msg,
"code": strconv.Itoa(status),
})
return nil
}}
注册方式(放在 app.Use() 最前面):
app.Use(JSONErrorHandler)
使用约定式提交(Conventional Commits)生成标准化的 Git 提交信息。适用于以下场景: (1) 暂存更改时需要提交信息; (2) 审查 diff 时希望获得结构化摘要; (3) 编写 PR 描述或变更日志(changelog)条目; (4) 用户明确要求“为本次更改编写提交信息”。 该功能会分析已暂存/未暂存的 diff,并生成按类型(type)划分范围的提交信息。
- 注意顺序:必须在
middleware.PopTransaction和middleware.Session之后?不,要更前——它得捕获它们抛出的错误 - 别用
c.Error(),它会触发默认 HTML 渲染;直接c.JSON()或手动WriteHeader + Encode更可控 - 如果用了
render包,c.Render(500, r.JSON, ...)也可,但需确保r.JSON是你自定义的 renderer,不是默认 plush
404 路由未匹配时为什么还是返回 HTML
因为 Buffalo 的 RouteNotFoundError 是个特殊 error,它不经过 echo 的 HTTPError 流程,而是被 App.ServeHTTP 单独 catch 并调用 app.ErrorHandler ——而这个 handler 默认就是 plush 渲染器。
解决办法只有两个:
- 在
app.go初始化后立刻覆盖:app.ErrorHandler = func(status int, err error, c buffalo.Context) { c.JSON(status, map[string]string{"error": "Not Found"}) } - 或者更彻底:删掉
app.ErrorHandler赋值,完全依赖上面写的JSONErrorHandler中间件(它已处理buffalo.RouteNotFoundError类型)
漏掉这一环,哪怕其他错误都 JSON 了,404 依然 HTML。
panic 和数据库错误怎么也进 JSON 流水线
panic 会被 middleware.Recovery 捕获,默认打堆栈并返回 HTML。DB 层错误(比如 pop.Error)若没被 action 显式 return,也会一路透传到 Recovery。
关键点:
-
middleware.Recovery必须在JSONErrorHandler之后注册,否则 Recovery 先吃掉 panic 并吐 HTML,你的中间件就收不到 - 把
middleware.Recovery的Options改掉:middleware.Recovery(func(r io.Writer, err interface{}) { /* 自己写 JSON 输出 */ }) - 或者干脆不用 Recovery,在
JSONErrorHandler里加一层recover()(不推荐,职责混杂)
真正干净的做法:禁用默认 Recovery,只保留你自己的中间件,并在里面统一处理 panic、HTTPError、RouteNotFoundError 和业务 error —— 因为 Buffalo 不强制你用它的错误体系,你完全可以绕过。


















