Golang使用swagger生成api接口文档
现在大部分应用都是前后端分离的项目,那么,前端和后端交互只有通过api接口文档来实现。swagger可以根据注释来生成api接口文档。
预备知识
- gin
- gorm
- mysql
添加依赖
go get github.com/swaggo/swag/cmd/swag
go get github.com/gin-gonic/gin
go get github.com/go-sql-driver/mysql
go get gorm.io/gorm
go get gorm.io/gorm/logger
go get github.com/swaggo/gin-swagger
go get github.com/swaggo/files
swag.exe
gin+gorm创建一个CRUD项目
model.go
package main
import "github.com/jinzhu/gorm"
type Post struct {
gorm.Model
Title string `gorm:"type:varchar(100);" josn:"title" example:"title" binding:"required"`
Des string `gorm:"type:varchar(100);" josn:"des" example:"desc" binding:"required"`
Status string `gorm:"type:varchar(200);" josn:"status" example:"Active"`
}
type Response struct {
Msg string
Data interface{}
}
api.go
package main
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
//查询
func Posts(c *gin.Context) {
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
offset, _ := strconv.Atoi(c.DefaultQuery("offset", "0"))
var posts []Post
db.Limit(limit).Offset(offset).Find(&posts)
c.JSON(http.StatusOK, gin.H{
"messege": "",
"data": posts,
})
}
func Show(c *gin.Context) {
post := getById(c)
c.JSON(http.StatusOK, gin.H{
"messege": "",
"data": post,
})
}
func Store(c *gin.Context) {
var post Post
if err := c.ShouldBindJSON(&post); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"messege": err.Error(),
"data": "",
})
return
}
post.Status = "Active"
db.Create(&post)
}
//删除
func Delete(c *gin.Context) {
post := getById(c)
if post.ID == 0 {
return
}
db.Unscoped().Delete(&post)
c.JSON(http.StatusOK, gin.H{
"messege": "deleted successfuly",
"data": "",
})
}
//更新
func Update(c *gin.Context) {
oldpost := getById(c)
var newpost Post
if err := c.ShouldBindJSON(&newpost); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"messege": err.Error(),
"data": "",
})
return
}
oldpost.Title = newpost.Title
oldpost.Des = newpost.Des
if newpost.Status != "" {
oldpost.Status = newpost.Status
}
db.Save(&oldpost)
c.JSON(http.StatusOK, gin.H{
"messege": "Post has been updated",
"data": oldpost,
})
}
//根据id查询
func getById(c *gin.Context) Post {
id := c.Param("id")
var post Post
db.First(&post, id)
if post.ID == 0 {
c.JSON(http.StatusOK, gin.H{
"messege": "Post not found",
"data": "",
})
}
return post
}
main.go
package main
import (
"github.com/gin-gonic/gin"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
// 实例化gorm.DB
var db *gorm.DB = nil
var err error
func main() {
// 与数据库建立连接
dsn := "root:960690@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
})
if err != nil {
panic(err.Error())
}
db.AutoMigrate(&Post{})
// 生成路由
server := gin.Default()
server.GET("/posts", Posts)
server.GET("/posts/:id", Show)
server.POST("/posts", Store)
server.PATCH("/posts/:id", Update)
server.DELETE("/posts/:id", Delete)
server.Run()
}
安装swag工具
go get github.com/swaggo/swag/cmd/swag
main函数添加注释
package main
import (
_ "go_pro/docs" // 必须导入docs,否则将报错: Failed to load API definition
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
// 实例化gorm.DB
var db *gorm.DB = nil
var err error
// @title gin+gorm 测试swagger (必填)
// @version 1.0 (必填)
// @description gin+gorm 测试swagger
// @license.name Apache 2.0
// @contact.name go-swagger文档
// @contact.url https://github.com/swaggo/swag/blob/master/README_zh-CN.md
// @host localhost:8080
// @BasePath /
func main() {
// 与数据库建立连接
dsn := "root:960690@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
})
if err != nil {
panic(err.Error())
}
db.AutoMigrate(&Post{})
server := gin.Default()
// 生成路由
server.GET("/posts", Posts)
server.GET("/posts/:id", Show)
server.POST("/posts", Store)
server.PATCH("/posts/:id", Update)
server.DELETE("/posts/:id", Delete)
// 生成api文档
server.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
server.Run()
}
go-swapper注解规范说明:
// @Summary 摘要
// @Description 描述
// @Description 接口的详细描述
// @Id 全局标识符
// @Version 接口版本号
// @Tags 接口分组,相当于归类
// @Accept json 浏览器可处理数据类型
// @Produce json 设置返回数据的类型和编码
// @Param 参数格式 从左到右:参数名、入参类型、数据类型、是否必填和注释 例:id query int true “ID”
// @Success 响应成功 从左到右:状态码、参数类型、数据类型和注释 例:200 {string} string “success”
// @Failure 响应失败 从左到右:状态码、参数类型、数据类型和注释 例:400 {object} string “缺少参数 ID”
// @Router 路由: 地址和http方法 例:/api/user/{id} [get]
// @contact.name 接口联系人
// @contact.url 联系人网址
// @contact.email 联系人邮箱
增加token验证方法
// @securityDefinitions.apikey ApiKeyAuth 安全方式
// @in header token携带的位置,这里是在header中
// @name Authorization heaer中的名称
添加接口的注释
package main
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
//查询
func Posts(c *gin.Context) {
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
offset, _ := strconv.Atoi(c.DefaultQuery("offset", "0"))
var posts []Post
db.Limit(limit).Offset(offset).Find(&posts)
c.JSON(http.StatusOK, gin.H{
"messege": "",
"data": posts,
})
}
// @Summary 查询
// @Description 查询
// @Tags posts
// @Accept json
// @Produce json
// @Param id path int true "pid"
// @Success 200 {object} Response
// @Failure 400 {object} Response
// @Failure 404 {object} Response
// @Failure 500 {object} Response
// @Router /posts/{id} [get]
func Show(c *gin.Context) {
post := getById(c)
c.JSON(http.StatusOK, gin.H{
"messege": "",
"data": post,
})
}
// @Summary 添加post
// @Description 添加post
// @Tags posts
// @Accept json
// @Produce json
// @Param content body string true "json"
// @Success 200 {object} Response
// @Failure 400 {object} Response
// @Failure 404 {object} Response
// @Failure 500 {object} Response
// @Router /posts [post]
func Store(c *gin.Context) {
var post Post
if err := c.ShouldBindJSON(&post); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"messege": err.Error(),
"data": "",
})
return
}
post.Status = "Active"
db.Create(&post)
}
//删除
func Delete(c *gin.Context) {
post := getById(c)
if post.ID == 0 {
return
}
db.Unscoped().Delete(&post)
c.JSON(http.StatusOK, gin.H{
"messege": "deleted successfuly",
"data": "",
})
}
//更新
func Update(c *gin.Context) {
oldpost := getById(c)
var newpost Post
if err := c.ShouldBindJSON(&newpost); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"messege": err.Error(),
"data": "",
})
return
}
oldpost.Title = newpost.Title
oldpost.Des = newpost.Des
if newpost.Status != "" {
oldpost.Status = newpost.Status
}
db.Save(&oldpost)
c.JSON(http.StatusOK, gin.H{
"messege": "Post has been updated",
"data": oldpost,
})
}
//根据id查询
func getById(c *gin.Context) Post {
id := c.Param("id")
var post Post
db.First(&post, id)
if post.ID == 0 {
c.JSON(http.StatusOK, gin.H{
"messege": "Post not found",
"data": "",
})
}
return post
}
参数param的几种类型 :
query 形如 /user?userId=1016
body 需要将数据放到 body 中进行请求
formData multipart/form-data* 请求
path 形如 /user/1016
header header头信息
格式化swag注解
swag fmt
格式化后注解被美化了
生成文档
swag init
将会生成一个docs文件夹,下面生成3个文件:
- docs\docs.go
- docs\swagger.json
- docs\swagger.yaml
swag init
测试
启动工程,在浏览器中输入地址:http://localhost:8080/swagger/index.html
POSTTry it outExecute
查看响应结果:
点击GET右侧下拉按钮,并输入pid进行查看:
在数据库中查看:
补充知识:Token验证
main函数注释追加:
// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name Authorization
路由函数注释追加:
// @Security ApiKeyAuth
补充知识:上传文件
路由函数注释追加:
// @Accept multipart/form-data
// @Param file formData file true "file"
详情请见
https://github.com/swaggo/swag/blob/master/README_zh-CN.md