初学时很多人每个 handler 都自己写一套错误返回:
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}不是不能写,但项目一大就会变成:
- 格式不统一
- 状态码策略不统一
- 日志记录不统一
- 调用栈埋点不统一
Gin 的一个好思路是:
handler 负责发现事故,中间件负责统一收口。
#1. 一个统一错误处理中间件
func ErrorHandler() gin.HandlerFunc {
return func(c *gin.Context) {
c.Next()
if len(c.Errors) == 0 {
return
}
if c.Writer.Written() {
return
}
err := c.Errors.Last().Err
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": err.Error(),
})
}
}#2. 业务 handler 里只上报错误
func getCheckpoint(c *gin.Context) {
id := c.Param("id")
if id != "A-17" {
c.Error(errors.New("卡口不存在"))
return
}
c.JSON(http.StatusOK, gin.H{
"id": id,
"status": "online",
})
}#3. 这套机制的心智
窗口发现事故
│
▼
把事故挂到 c.Errors
│
▼
统一事故处理中心读取 c.Errors
│
▼
按统一格式出事故回执这套做法的价值在于:
- 业务逻辑和错误包装解耦
- 错误格式统一
- 更容易接日志、告警、埋点