在做 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"`
}