不能直接在每个 handler 里重复写 ctx.JSON(200, map[string]interface{}{...}),因为会导致结构体不统一、状态码与业务码混用、中间件无法提取真实业务状态,且易引发前端解析混乱;必须通过自定义 Resp 结构体 + Success/Fail 封装函数 + 最终中间件统一输出来实现标准化响应。

为什么不能直接在每个 handler 里写 ctx.JSON(200, map[string]interface{}{"code":0,"data":x,"msg":"ok"})
重复写结构体、硬编码 code/msg 字段、漏设 HTTP 状态码、错误时返回 200 却塞进 {"code":500}——这些都会让前端解析混乱,也导致中间件(比如统一日志、监控)无法可靠提取业务状态。Iris 本身不提供默认响应包装,必须自己建模、拦截、标准化。
用自定义 Response 结构体 + 全局 ctx.Values().Set() 注入
别依赖全局变量或闭包传参,Iris 的 ctx.Values() 是请求生命周期内的安全存储区,比函数参数传递更解耦。
- 先定义统一结构:
type Resp struct { Code int `json:"code"` Msg string `json:"msg"` Data interface{} `json:"data,omitempty"` Time int64 `json:"time"` } - 封装两个核心函数:
func Success(ctx iris.Context, data interface{}) { ctx.StatusCode(200) ctx.Values().Set("resp", Resp{Code: 0, Msg: "success", Data: data, Time: time.Now().Unix()}) } func Fail(ctx iris.Context, code int, msg string) { ctx.StatusCode(code) ctx.Values().Set("resp", Resp{Code: code, Msg: msg, Time: time.Now().Unix()}) } - 在最后的中间件中统一输出:
app.Use(func(ctx iris.Context) { ctx.Next() if v := ctx.Values().Get("resp"); v != nil { ctx.JSON(200, v) return } ctx.Next() // 无 resp,继续走默认逻辑(比如静态文件) })
ctx.JSON() 被提前调用会导致封装失效
这是最常踩的坑:某个 handler 里写了 ctx.JSON(200, x) 或 ctx.Text(),之后中间件再想塞 resp 已经晚了——HTTP 头和 body 都发出去了。Iris 不会报错,但你的统一封装彻底失效。
- 所有 handler 必须只调用
Success()/Fail(),严禁直调ctx.JSON() - 检查第三方中间件(如 JWT 验证失败时的
ctx.StatusCode(401).Text("unauthorized")),要重写为调用Fail(ctx, 401, "unauthorized") - 用
app.OnErrorCode(404, ...)和app.OnPanic(...)补全异常路径,确保 404/500 也走同一套Resp结构
需要兼容 OpenAPI/Swagger 时字段名不能改
如果项目要生成 Swagger 文档,code/msg/data 这三个字段名是主流约定,但 Iris 默认 JSON 序列化不会自动转 camelCase。Go 的 json tag 必须显式写全:
type Resp struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data,omitempty"`
Time int64 `json:"time"`
}
别省略 json: tag,也别用 snake_case 或大驼峰——否则前端 SDK 自动生成器会找不到字段。


















