YApi结合swag管理go项目API文档
├── controller
│ ├── accounts.go
│ └── controller.go
├── docs
│ ├── docs.go
│ ├── swagger.json
│ └── swagger.yaml
├── go.mod
├── httputil
│ └── error.go
├── main.go
└── model
└── account.go
main.go
package main
import (
"demo/controller"
_ "demo/docs"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)
// @title Swagger Example API
// @version 1.0
// @description This is a sample server demo server.
// @termsOfService http://swagger.io/terms/
// @contact.name API Support
// @contact.url http://www.swagger.io/support
// @contact.email [email protected]
// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
// @host localhost:8080
// @BasePath /api/v1
func main() {
r := gin.Default()
c := controller.NewController()
v1 := r.Group("/api/v1")
{
accounts := v1.Group("/accounts")
{
accounts.GET(":id", c.ShowAccount)
accounts.POST("", c.AddAccount)
}
}
// 先用swag init 生成docs目录
// http://localhost:8080/swagger/index.html 可以看swagger ui的文档
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
r.Run(":8080")
}
controller/controller.go
package controller
// Controller example
type Controller struct {
}
// NewController example
func NewController() *Controller {
return &Controller{}
}
// Message example
type Message struct {
Message string `json:"message" example:"message"`
}
controller/accounts.go
package controller
import (
"demo/httputil"
"demo/model"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
// ShowAccount godoc
// @Summary Show a account
// @Description get string by ID
// @Tags accounts
// @Accept json
// @Produce json
// @Param id path int true "Account ID"
// @Success 200 {object} model.Account
// @Failure 400 {object} httputil.HTTPError
// @Failure 404 {object} httputil.HTTPError
// @Failure 500 {object} httputil.HTTPError
// @Router /accounts/{id} [get]
func (c *Controller) ShowAccount(ctx *gin.Context) {
id := ctx.Param("id")
aid, err := strconv.Atoi(id)
if err != nil {
httputil.NewError(ctx, http.StatusBadRequest, err)
return
}
account, err := model.AccountOne(aid)
if err != nil {
httputil.NewError(ctx, http.StatusNotFound, err)
return
}
ctx.JSON(http.StatusOK, account)
}
// AddAccount godoc
// @Summary Add a account
// @Description add by json account
// @Tags accounts
// @Accept json
// @Produce json
// @Param account body model.AddAccount true "Add account"
// @Success 200 {object} model.Account
// @Failure 400 {object} httputil.HTTPError
// @Failure 404 {object} httputil.HTTPError
// @Failure 500 {object} httputil.HTTPError
// @Router /accounts [post]
func (c *Controller) AddAccount(ctx *gin.Context) {
var addAccount model.AddAccount
if err := ctx.ShouldBindJSON(&addAccount); err != nil {
httputil.NewError(ctx, http.StatusBadRequest, err)
return
}
if err := addAccount.Validation(); err != nil {
httputil.NewError(ctx, http.StatusBadRequest, err)
return
}
// Name以外的字段仅用来演示
account := model.Account{
Name: addAccount.Name,
}
lastID, err := account.Insert()
if err != nil {
httputil.NewError(ctx, http.StatusBadRequest, err)
return
}
account.ID = lastID
ctx.JSON(http.StatusOK, account)
}
httputil/error.go
package httputil
import "github.com/gin-gonic/gin"
// NewError example
func NewError(ctx *gin.Context, status int, err error) {
er := HTTPError{
Code: status,
Message: err.Error(),
}
ctx.JSON(status, er)
}
// HTTPError example
type HTTPError struct {
Code int `json:"code" example:"400"`
Message string `json:"message" example:"status bad request"`
}
model/account.go
package model
import (
"errors"
"fmt"
uuid "github.com/satori/go.uuid"
)
// Account example
type Account struct {
ID int `json:"id" example:"1" format:"int64"`
Name string `json:"name" example:"account name"`
UUID uuid.UUID `json:"uuid" example:"550e8400-e29b-41d4-a716-446655440000" format:"uuid"`
}
// example
var (
ErrNameInvalid = errors.New("name is empty")
ErrNoRow = errors.New("no rows in result set")
)
// AddAccount example
// Name以外的字段仅用来测试 struct tag 的作用
type AddAccount struct {
Name string `json:"name" example:"Tom" format:"string" binding:"required" minLength:"1" maxLength:"16"` // 用户名
Age int `json:"age" example:"10" binding:"required" minimum:"1" maximum:"150" default:"10"` // 年龄
Height float64 `json:"height" example:"1.80" binding:"required" minimum:"0.0" maximum:"9.99"` // 身高,单位米
Status string `json:"status" enums:"healthy,ill"` // 状态
}
// Validation example
func (a AddAccount) Validation() error {
switch {
case len(a.Name) == 0:
return ErrNameInvalid
default:
return nil
}
}
// AccountOne example
func AccountOne(id int) (Account, error) {
for _, v := range accounts {
if id == v.ID {
return v, nil
}
}
return Account{}, ErrNoRow
}
// Insert example
func (a Account) Insert() (int, error) {
accountMaxID++
a.ID = accountMaxID
a.Name = fmt.Sprintf("account_%d", accountMaxID)
accounts = append(accounts, a)
return accountMaxID, nil
}
var accountMaxID = 3
var accounts = []Account{
{ID: 1, Name: "account_1"},
{ID: 2, Name: "account_2"},
{ID: 3, Name: "account_3"},
}
在项目根目录下执行以下命令生成docs目录
swag init
打开localhost:40001
登录YApi
在上图可以看到,model.AddAccount
结构体附属的struct tag 和字段的注释已经变成文档的一部分了
同时还可以像postman一样测试接口,其他功能就不说了,留待你们自己挖掘
在go项目中,开发人员只要把代码的注解写好,然后用swag生成相关文件,YApi导入json文件,整个项目的接口管理变的清晰简单。不过文档没有swagger ui展示的那么详细,可以看情况搭配使用。