前言
在前面几篇中,我们已经完成了用户模块的注册、查询、更新、删除,以及分页查询接口。过程中我们也写了分页结构体、统一的接口返回方法,看起来也还挺顺手的。
但写着写着就会发现,有些东西我们在每个接口里都在重复写,比如:
- 分页的
page/page_size/total/list
,接口一多就得一遍遍写 - 接口返回结构也差不多,都是
code + msg + data
,还不如封一下 - 错误信息如果不统一,前端调用时也不太好处理
所以今天我们就来干脆一步到位:
把分页结构、响应结构、错误码这些**"所有接口都绕不开的东西"**,统一抽出来封装成一个响应模块。
模板结构:core/response 模块
我们将通用的响应逻辑提取到一个独立模块,命名为:
bash
core/response
模块职责非常明确:
- 管理所有接口响应结构(code/msg/data)
- 提供 Success / Fail / WithCode 等统一返回方法
- 封装错误码常量与提示信息映射
- 提供分页响应结构
PageData
项目结构如下:
go
gin-learn-notes/
└── core/
└── response/
├── code.go // ✅ 错误码常量与提示信息
├── response.go // ✅ 通用响应结构体 + 返回方法
└── page.go // ✅ 分页响应结构封装(PageData)
如果我们后续如果需要扩展,比如加 trace_id、响应耗时统计、多语言支持,也可以继续在这个模块里扩展,结构非常灵活。
第一步:创建响应模块目录 & 错误码定义
创建目录
我们先在项目中创建响应模块的存放路径:
bash
mkdir -p core/response
编写 core/response/code.go
在这个文件中我们统一管理所有的错误码。先定义一些常用的通用错误码:
go
package response
const (
CodeSuccess = 0 // 成功
ParamError = 10001 // 参数错误
NotFound = 10002 // 数据不存在
DBError = 10003 // 数据库错误
Unauthorized = 10004 // 未授权/未登录
ServerError = 10005 // 服务内部错误
)
第二步:统一响应结构封装(response.go
)
创建文件:
bash
touch core/response/response.go
然后我们定义一个标准的响应结构体 Result
,并提供常用的响应函数:
go
package response
import (
"github.com/gin-gonic/gin"
"net/http"
)
type Result struct {
Code int `json:"code"` // 状态码
Msg string `json:"msg"` // 提示信息
Data interface{} `json:"data,omitempty"` // 返回数据(可选)
}
通用响应方法
go
// JSON 是最底层响应封装方法
func JSON(c *gin.Context, code int, msg string, data interface{}) {
c.JSON(http.StatusOK, Result{
Code: code,
Msg: msg,
Data: data,
})
}
// Success 成功响应(含数据)
func Success(c *gin.Context, data interface{}) {
JSON(c, CodeSuccess, "success", data)
}
// Fail 失败响应(含错误码与信息)
func Fail(c *gin.Context, code int, msg string) {
JSON(c, code, msg, nil)
}
第三步:提取分页响应结构体(page.go)
在上一篇中我们在 utils/paginate.go
中写了分页响应结构体:
go
type PageData struct {
List interface{} `json:"list"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
它用于标准化所有分页接口的返回结果,避免每个接口都重复写 list + total + page + page_size
。
提取到 core 模块
现在我们将它挪到统一响应模块中,新建文件:
bash
touch core/response/page.go
内容如下:
go
package response
type PageData struct {
List interface{} `json:"list"` // 数据列表
Total int64 `json:"total"` // 总数
Page int `json:"page"` // 当前页
PageSize int `json:"page_size"` // 每页条数
}
使用示例
go
response.Success(c, response.PageData{
List: users,
Total: total,
Page: req.Page,
PageSize: req.PageSize,
})
这样所有分页接口就可以统一使用 PageData
,无需在 controller 中重复拼接,接口响应结构也更加统一、易读、标准化 .
示例:如何在控制器中使用统一响应模块
统一封装完响应结构之后,我们在控制器中就可以这样调用,非常清爽、标准:
go
import (
"gin-learn-notes/core/response"
"github.com/gin-gonic/gin"
)
成功响应(带数据)
go
response.Success(c, gin.H{
"user_id": user.ID,
})
返回示例:
json
{
"code": 0,
"msg": "success",
"data": {
"user_id": 1
}
}
失败响应(带错误码 + 提示)
go
response.Fail(c, response.CodeParamError, "参数不合法")
返回示例:
json
{
"code": 10001,
"msg": "参数不合法",
"data": null
}
模块可复用性 & 扩展性
我们现在封装的这个 core/response/
模块,其实并不只属于当前这个项目。
我们甚至可以把它整个目录 复制粘贴到任何一个 Gin 项目中直接使用,完全不依赖业务代码,极其通用。
甚至我们也可以将其提取为自己的 Git 子模块或私有模块,比如:
bash
github.com/myname/gin-core/response
以后每个新项目 go get
一下,直接引用,响应结构、错误码、分页一应俱全,开箱即用。
项目结构继续演进(集成响应模块)
go
gin-learn-notes/
├── config/
│ └── database.go // 数据库连接配置
│
├── controller/
│ ├── hello.go
│ ├── index.go
│ └── user.go // 用户模块控制器
│
├── core/
│ └── response/ // ✅ 响应模块(统一响应结构、错误码、分页结构体)
│ ├── code.go // 错误码定义
│ ├── page.go // 分页响应结构体
│ └── response.go // 通用响应结构体 + 返回方法
│
├── model/
│ └── user.go // 用户数据模型
│
├── request/
│ ├── page_request.go // 通用分页请求结构体
│ └── user_request.go // 用户模块请求结构体
│
├── router/
│ └── router.go // 路由注册
│
├── service/
│ └── user_service.go // 用户业务逻辑处理
│
├── utils/
│ ├── paginate.go // 泛型分页方法
│ ├── response.go // 已封装的通用响应(可逐步迁移至 core/response)
│ └── validator.go // 参数校验错误翻译
│
├── main.go // 项目入口
├── go.mod
├── .gitignore
└── README.md
最后
到目前为止,我们已经围绕 Gin 框架完成了多个模块的实战开发与封装,包括:
- ✅ 用户模块的增删改查接口(全 POST 实现)
- ✅ 通用分页请求参数与分页响应结构的封装
- ✅ 统一响应格式结构体 + 错误码管理模块
- ✅ 控制器职责划分清晰、代码结构逐步模块化
在拥有了结构清晰的项目基础后,下一步我们将实现日志模块的封装与接入。
日志是后端系统不可或缺的模块,它直接影响到问题排查与系统可观测性,我们将继续秉持"可复用"的思路,把日志模块也封装成一个独立组件,服务整个项目。
本篇对应代码提交记录
commit: 6bc8562fb0d6d2b818aef48f722fddc087c58e85
👉 GitHub 源码地址:github.com/luokakale-k...