在做 API 接口開發時, 一般會統一 API 返回格式, 例如
{
"code": 200,
"data": {
//xxxxx
//xxxxx
},
"message": "OK"
}
在后端代碼定義中, 也會定義一個結構體來對應這種結構, 並且, 由於 data 字段里的數據是未知的(與具體業務相關), 所以會定義一個 interface 來接收
type ApiResponse struct {
Code int `json:"code"`
Msg string `json:"message"`
Data interface{} `json:"data"`
}
然后根據具體業務響應, 向 data 傳入不同的模型, 比如
c.JSON(200, ApiResponse{200, "OK", User})
但是這里有個很大的問題, swagger 文檔中, 這個接口的返回值該怎么定義?
// @Summary 獲取用戶信息
// ...
// ...
// @Success 200 {object} ApiResponse "ok"
func GetUser(c *gin.Context) {
xxxx
}
如果這樣定義, 生成的文檔會是下面這樣, 因為原始 ApiResponse 就是一個 interface, 所以是空

但是這樣的文檔寫出來就沒什么意義了, 大多數的做法就是會專門定義一個用於 swagger 顯示的結構體, 類似這樣
type UserResponse struct {
Code int `json:"code"`
Msg string `json:"message"`
Data User `json:"data"`
}
雖然效果有了, 但是這樣無疑增加了巨多的工作量, 讓寫代碼變得索然無味, 翻看 swaggo/swag 的文檔, 發現支持了替換字段的方式, 可以完美解決現在這種問題, 效果如下

下面是測試代碼
package main
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
// @title Swagger Example API
// @version 1.0
// @description This is a sample server Petstore server.
// @termsOfService http://swagger.io/terms/
// @contact.name API Support
// @contact.url http://www.swagger.io/support
// @contact.email support@swagger.io
// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
// @host petstore.swagger.io
// @BasePath /v2
func main() {
r := gin.New()
r.GET("/user/:id", GetUser)
}
// @Summary 獲取用戶信息
// @Description get User by ID
// @ID get-user-by-id
// @Accept json
// @Produce json
// @Param id path int true "用戶 id"
// @Success 200 {object} ApiResponse{data=User} "ok"
// @Router /user/{id} [get]
func GetUser(c *gin.Context) {
resp := new(ApiResponse)
paramID := c.Param("id")
uid, _ := strconv.Atoi(paramID)
user := User{
ID: uid,
Name: "張三",
}
resp.Code = 200
resp.Msg = "OK"
resp.Data = user
c.JSON(http.StatusOK, resp)
}
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
type ApiResponse struct {
Code int `json:"code"`
Msg string `json:"message"`
Data interface{} `json:"data"`
}
