Go 后端错误响应统一化

January 5, 2026
3 min read
By devshan

Table of Contents

This is a list of all the sections in this post. Click on any of them to jump to that section.

背景

Go 核心代码中存在大量重复的错误响应模式:

c.JSON(http.StatusBadRequest, gin.H{"error": "some error message"})

这种模式散落在 10 个 API 文件中,共计 150+ 处,存在以下问题:

  • 代码重复,不符合 DRY 原则
  • 如需修改错误响应格式,需要改动大量文件
  • 容易出现不一致(如有些用 StatusInternalServerError,有些用 500)

实现方案

1. 创建统一错误响应函数

utils.go 中添加:

// ==================== 统一错误响应 ====================
 
// respondError 返回错误响应
func respondError(c *gin.Context, status int, msg string) {
    c.JSON(status, gin.H{"error": msg})
}
 
// respondBadRequest 返回 400 错误
func respondBadRequest(c *gin.Context, msg string) {
    respondError(c, http.StatusBadRequest, msg)
}
 
// respondNotFound 返回 404 错误
func respondNotFound(c *gin.Context, msg string) {
    respondError(c, http.StatusNotFound, msg)
}
 
// respondServerError 返回 500 错误
func respondServerError(c *gin.Context, msg string) {
    respondError(c, http.StatusInternalServerError, msg)
}
 
// respondUnauthorized 返回 401 错误
func respondUnauthorized(c *gin.Context, msg string) {
    respondError(c, http.StatusUnauthorized, msg)
}
 
// respondForbidden 返回 403 错误
func respondForbidden(c *gin.Context, msg string) {
    respondError(c, http.StatusForbidden, msg)
}
 
// respondConflict 返回 409 错误
func respondConflict(c *gin.Context, msg string) {
    respondError(c, http.StatusConflict, msg)
}
 
// respondServiceUnavailable 返回 503 错误
func respondServiceUnavailable(c *gin.Context, msg string) {
    respondError(c, http.StatusServiceUnavailable, msg)
}

2. 批量替换

文件替换数量
server.go13
e2e.go17
ffmpeg.go4
files.go25+
folders.go17+
logs.go4
middleware.go1
orphan.go20+
send.go25+
thumbnail.go22

替换模式:

// Before
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
 
// After
respondBadRequest(c, "invalid request")

技术决策

已实施

P1: 统一错误响应 - 已完成

  • 收益:代码去重、格式统一、易于维护
  • 成本:一次性批量替换工作

评估后放弃

loadMeta + saveMeta 事务封装 - 不实施

  • 原因:每个 API 在 load 和 save 之间都有不同的业务逻辑
  • 如果封装成事务函数,需要传入大量回调或使用复杂的泛型
  • Go 没有 try-catch,事务回滚逻辑会很复杂
  • 当前代码已经足够清晰,强制封装反而增加复杂度

MetadataCache TTL 移入配置 - 暂不实施

  • 当前硬编码值:TTL 30s,清理间隔 5m
  • 这些值在开发阶段很少需要调整
  • 移入配置增加复杂度,收益不大
  • 如果未来需要,可以再添加

验证

每批替换后执行:

go vet ./...
go build -o core.exe ./cmd/server/

全部通过,无编译错误。

影响范围

  • 修改文件:11 个(10 个 API 文件 + utils.go)
  • 功能变化:无,纯重构
  • API 兼容性:完全兼容,响应格式未变