c.JSON()是Fiber框架中返回标准JSON响应的最常用方法,自动设置Content-Type并序列化数据,内部默认使用fastjson提升性能;需注意字段导出、omitempty标签、header设置时机及序列化错误处理。

用 c.JSON() 返回标准 JSON 响应
直接调用上下文的 c.JSON() 方法是最常用、最安全的方式,它会自动设置 Content-Type: application/json,并序列化结构体或 map。Fiber 内部使用 fastjson(默认)或可替换为 encoding/json,性能比原生 json.Marshal 更高。
常见错误是手动调用 json.Marshal 后用 c.SendString(),这样会漏掉 Content-Type 头,前端可能解析失败;或者忘记检查 error 导致 panic。
c.JSON(200, map[string]interface{}{"ok": true, "data": []int{1, 2, 3}})- 返回结构体时,字段必须首字母大写且带
jsontag 才能被序列化:type User { Name string `json:"name"` } - 如果结构体含
time.Time,默认序列化为 RFC3339 字符串,无需额外处理
处理 JSON 序列化失败的场景
c.JSON() 内部不暴露序列化错误,出错时直接返回 500。当数据含不支持类型(如 func、chan、循环引用指针)时,服务看似“静默失败”,但日志里会出现 json: unsupported type 类似报错。
建议在开发期加一层防御:对关键响应数据先用 json.Marshal 预检,或启用 Fiber 的 DisableStartupMessage 并配合自定义错误中间件捕获 panic。
立即学习“go语言免费学习笔记(深入)”;
- 避免传入未初始化的 slice 或 map:空值会被转成
null,不是[]或{},前端需兼容 - 若需控制
nil字段行为(比如忽略而非输出"field": null),用omitemptytag:Age *int `json:"age,omitempty"` - 时间字段想输出 Unix 时间戳?不能靠 tag,得提前转换:
CreatedAt: user.CreatedAt.Unix()
返回带状态码和自定义 Header 的 JSON
仅靠 c.JSON() 无法附加额外 header(比如 X-Request-ID 或 Cache-Control)。必须先调用 c.Status() 和 c.Set(),再调用 c.JSON() —— 注意顺序:header 必须在 c.JSON() 之前设置,否则会被覆盖。
- 正确顺序:
c.Status(201).Set("X-Trace-ID", traceID).JSON(map[string]string{"id": "abc"}) - 错误写法:
c.JSON(...); c.Set(...)—— 此时响应已写出,Set()无效 - 若要统一加 header(如 CORS),应在中间件里用
c.Set(),而不是每个 handler 重复写
为什么不用 c.Send() 或 c.SendString() 返回 JSON
这两个方法不会设置 Content-Type,浏览器或 axios 默认按 text/plain 解析,导致 response.json() 报错 Unexpected token;Node.js 客户端也可能因 MIME 类型不符拒绝解析。
更隐蔽的问题是:c.SendString() 对中文等非 ASCII 字符默认不转义,而 c.JSON() 会按 JSON 规范正确编码 Unicode,避免乱码或解析中断。
- 绝对不要写:
c.SendString(`{"msg":"你好"}`)—— 缺少 header,且无编码保障 - 也不要手动拼接:
c.Send([]byte(`{"code":200}`))—— 同样没 header,还绕过序列化校验 - 例外:极低延迟场景下需预序列化 JSON 字节缓存,此时应自己
Set("Content-Type", "application/json"),但多数业务不需要
c.JSON() 看似简单,真正容易出问题的地方在于:结构体字段导出规则、nil 值语义、header 设置时机,以及对底层序列化行为的误判。别把它当成黑盒,尤其在调试前端报 Unexpected end of JSON input 时,优先查后端是否真的发出了合法 JSON。


















