中间件
统一响应
ResponseMiddleware 自动处理未输出的响应和错误
统一响应
ResponseMiddleware 在请求处理完成后,统一检查和处理响应输出,确保所有请求都有正确的响应格式。
ResponseMiddleware
func ResponseMiddleware(ctx *gin.Context)实现:
func ResponseMiddleware(ctx *gin.Context) {
ctx.Next() // 先执行后续处理器
// 检查响应是否已写入
if !ctx.Writer.Written() {
// 情况1: 存在错误
if ctx.Errors != nil && len(ctx.Errors) > 0 {
var getErr *xError.Error
// 尝试解析为自定义错误类型
if errors.As(ctx.Errors.Last(), &getErr) && getErr.ErrorCode != nil {
xResult.Error(ctx, getErr.ErrorCode, getErr.ErrorMessage, getErr.Data)
} else {
// 通用服务器内部错误
xResult.Error(ctx, xError.ServerInternalError, ...)
}
ctx.Abort()
} else {
// 情况2: 无错误但也无响应(排除重定向)
if ctx.Writer.Status() != 301 && ctx.Writer.Status() != 302 {
xResult.Error(ctx, xError.DeveloperError,
"没有正常输出信息或报错信息,请检查代码逻辑「开发者错误」",
nil)
ctx.Abort()
}
}
}
}处理流程
三种处理场景
场景一:正常响应
Handler 已调用 xResult.Success() 输出响应:
func GetUser(ctx *gin.Context) {
user := userService.FindByID(ctx, id)
// 已输出响应,ResponseMiddleware 不做处理
xResult.Success(ctx, user)
}场景二:错误响应
Handler 通过 ctx.Error() 添加错误:
func GetUser(ctx *gin.Context) {
user, err := userService.FindByID(ctx, id)
if err != nil {
// 添加错误到 ctx.Errors,ResponseMiddleware 自动处理
ctx.Error(xError.NewError(ctx.Request.Context(), xError.NotFound, "用户不存在", true))
return
}
xResult.Success(ctx, user)
}输出:
{
"context": "abc123",
"output": "NOT_EXIST",
"code": 40004,
"message": "数据不存在",
"error_message": "用户不存在"
}场景三:开发者错误
Handler 既没有输出响应,也没有添加错误:
func GetUser(ctx *gin.Context) {
user := userService.FindByID(ctx, id)
// 忘记输出响应!ResponseMiddleware 会返回 DeveloperError
_ = user
}输出:
{
"context": "abc123",
"output": "DEVELOPER_ERROR",
"code": 50001,
"message": "开发者错误",
"error_message": "没有正常输出信息或报错信息,请检查代码逻辑「开发者错误」"
}错误类型处理
xError.Error 类型
如果错误是 xError.Error 类型,使用其错误码和消息:
// 创建带错误码的错误
err := xError.NewError(ctx.Request.Context(), xError.ParameterError, "参数格式错误", true)
ctx.Error(err)其他错误类型
如果是普通 error,使用 ServerInternalError:
// 普通错误会被包装为 ServerInternalError
ctx.Error(errors.New("database connection failed"))使用示例
基础配置
func main() {
router := gin.Default()
// 注册统一响应中间件
router.Use(xMiddle.ResponseMiddleware)
router.GET("/api/users/:id", getUser)
router.Run(":8080")
}完整示例
package handler
import (
xError "github.com/bamboo-services/bamboo-base-go/common/error"
xResult "github.com/bamboo-services/bamboo-base-go/major/result"
"github.com/gin-gonic/gin"
)
func GetUser(ctx *gin.Context) {
id := ctx.Param("id")
// 参数校验
if id == "" {
ctx.Error(xError.NewError(ctx.Request.Context(), xError.ParameterError, "用户ID不能为空", true))
return
}
user, err := userService.FindByID(ctx, id)
if err != nil {
// 业务错误
ctx.Error(xError.NewError(ctx.Request.Context(), xError.NotFound, "用户不存在", true))
return
}
// 成功响应
xResult.Success(ctx, user)
}带数据的错误
// 错误响应中携带额外数据
ctx.Error(xError.NewErrorHasData(
ctx.Request.Context(),
xError.ParameterError,
"参数校验失败",
true,
nil,
map[string]string{
"username": "长度必须在 3-20 之间",
"email": "格式不正确",
},
))输出:
{
"context": "abc123",
"output": "PARAMETER_ERROR",
"code": 40000,
"message": "参数错误",
"error_message": "参数校验失败",
"data": {
"username": "长度必须在 3-20 之间",
"email": "格式不正确"
}
}重定向处理
重定向响应(301/302)不会被 ResponseMiddleware 处理:
func RedirectHandler(ctx *gin.Context) {
// 重定向不会触发 DeveloperError
ctx.Redirect(302, "/new-location")
}