直接return map[string]interface{}会导致错误路径失控、分页结构重复、中间件无法统一加时间戳、前后端对code语义混淆;应改用responsewrapper中间件+success工具函数封装成功响应,错误则分业务错误、系统错误、jwt错误三层独立处理。

为什么直接 return map[string]interface{} 会出问题
很多刚用 Fiber 的人会在 handler 里写 ctx.JSON(200, map[string]interface{}{"code": 0, "message": "ok", "data": user}),短期看着没问题,但很快就会踩坑:错误路径没人管、分页接口要重复写结构、中间件没法统一加 timestamp、前端遇到 code: 500 和 code: 1001 完全分不清是系统异常还是业务校验失败。
用 ResponseWrapper 中间件自动包装成功响应
Fiber 没有 @ControllerAdvice 那套机制,得靠中间件 + 自定义返回函数。核心思路是:只在成功路径走中间件封装,异常走单独的错误处理器,避免混淆。
- 定义统一响应结构:
type Response struct { Code int `json:"code"` Message string `json:"message"` Data interface{} `json:"data,omitempty"` Timestamp int64 `json:"timestamp"` } - 写一个
Success(data interface{}) *Response工具函数,固定Code: 0、填入当前时间戳 - 注册中间件,在
ctx.Next()后检查状态码是否为 200 且未写过 body:func ResponseWrapper() fiber.Handler { return func(c *fiber.Ctx) error { if c.Response().StatusCode() == 200 && !c.Response().Written() { // 假设你把原始数据存在 c.Locals("response_data") 里 data := c.Locals("response_data") return c.JSON(200, Success(data)) } return c.Next() } } - 在 handler 里不直接
ctx.JSON,而是c.Locals("response_data", user),再调c.Next()
全局错误处理必须和成功路径解耦
Fiber 的 app.Use(func(c *fiber.Ctx) error { ... }) 是最后兜底,但不能在这里统一格式——因为 401/404/500 这些 HTTP 状态码本身就有语义,强行改成 {"code":401,"message":"unauthorized"} 会掩盖协议层意图。正确做法是分两层:
- 业务错误(如参数校验失败、权限不足):主动调
ctx.Status(400).JSON(Error(1002, "用户名已存在")) - 系统错误(panic、未捕获异常):用
app.Use(func(c *fiber.Ctx) error { ... })捕获,记录日志后返回Error(500, "服务暂时不可用"),不要改 HTTP 状态码 - JWT 校验失败这类框架级错误:fiber-jwt 默认返回
{"message":"Invalid or expired JWT"},想统一就得在jwt.New()里传自定义Config.ErrorHandler,手动转成你的Response结构
别忽略 Content-Type 和 JSON 编码细节
Fiber 默认用 encoding/json,但如果你用了 fiber.Config{JSONEncoder: ...} 替换为 easyjson 或 ffjson,要注意这些库对空值、time.Time、nil interface{} 的处理可能不一致。特别是 Data 字段设为 interface{} 时,某些编码器会把 nil 编成 null,而前端可能期望字段直接消失(靠 omitempty)。实测下来最稳的方式是:
- 所有 handler 返回前显式调
c.Set("Content-Type", "application/json; charset=utf-8") -
Data字段保持interface{},但业务层确保传进去的是具体类型(比如*User而不是nil),避免编码器猜错 - 如果用了 OpenAPI 自动生成(比如 oapi-codegen + fiber-server),生成的 handler 签名里
data是强类型,这时反而不能直接塞map[string]interface{},否则编译不过
ctx.JSON(200, ...),整个链路就断了。上线前最好加个单元测试,遍历所有路由,检查响应体是否都含 code 和 timestamp 字段。大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











