1.gin框架图解概览
2.gin框架介绍
Gin 是一个用 Go (Golang) 编写的 web 框架。 它是一个类似于 martini 但拥有更好性能的 API 框架,由于 httprouter,速度提高了近 40 倍。是一款不错的web框架。
特性如下所示:
快速
--基于 Radix 树的路由,小内存占用。没有反射。可预测的 API 性能。
支持中间件
--传入的 HTTP 请求可以由一系列中间件和最终操作来处理。 例如:Logger,Authorization,GZIP,最终操作DB。
Crash 处理
--Gin 可以 catch 一个发生在 HTTP 请求中的 panic 并 recover 它。这样,你的服务器将始终可用。
JSON 验证
--Gin 可以解析并验证请求的 JSON,例如检查所需值的存在。
路由组
--更好地组织路由。是否需要授权,不同的 API 版本…… 此外,这些组可以无限制地嵌套而不会降低性能。
错误管理
--Gin 提供了一种方便的方法来收集 HTTP 请求期间发生的所有错误。最终,中间件可以将它们写入日志文件,数据库并通过网络发送。
内置渲染
--Gin 为 JSON,XML 和 HTML 渲染提供了易于使用的 API。
可扩展性
--中间件的扩展。
3.gin代码框架功能介绍
3.1 创建一个http服务。
如下示例为一个简便的http服务,以及响应信息
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.POST("/post", func(c *gin.Context) {
c.String(http.StatusOK, "Receive a post request !!!")
})
r.Run()
}
响应:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Fri, 16 Oct 2020 08:41:34 GMT
Content-Length: 28
Connection: close
"Receive a post request !!!"
3.2.gin路由介绍:
在创建http服务时,访问都是基于路由进行请求消息传达,针对路由的使用分为路由,以及路由组使用。
3.2.1,单个路由使用实例。
如下代码所示:创建了POST请求的路由访问,通过调用注册的处理方法,处理相关的请求,除了post路由还有如下相关的路由请求,这里不做演示,根据具体代码可自行编写demo测试。
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.POST("/post", func(c *gin.Context) {
c.JSON(http.StatusOK, "Receive a post request !!!")
})
r.Run()
}
3.2.2.路由组的使用
路由组,始终是在一个固定的路由路径下可以一批对应的路由信息,从而通过不同的路由来处理不同的请求访问。
如下代码所示:路由组,在gin中提供了Group方法,方法的定义:func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {},参数分别为路由组名称,回调方法列表,在接收到对应路由请求,回调用对应的方法列表预先进行对应请求的处理,等价后面的gin中提供的中间件功能,后续会介绍该功能的使用方法。示例如下:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
func OutFun() gin.HandlerFunc {
return func(c *gin.Context) {
fmt.Println("Request client ip is ", c.ClientIP())
c.Next()
}
}
func TotFun() gin.HandlerFunc {
return func(c *gin.Context) {
fmt.Println("Request url is ", c.Request.URL)
c.Next()
}
}
func main() {
r := gin.Default()
rh := r.Group("/user", OutFun(), TotFun())
{
rh.POST("/post", func(c *gin.Context) {
c.JSON(http.StatusOK, "This is /user/post request !")
})
rh.GET("/get", func(c *gin.Context) {
c.JSON(http.StatusOK, "This is /user/get request !")
})
}
r.Run()
}
如上代码所示,在创建group路由组,自定义中间件方法在访问具体的路由前先进行相关处理,后在进行具体的请求处理,具体处理可行自行测试。
如下是gin提供路由方法,具体使用如同2.2.1.代码示例即可。
3.2.3,gin提供的其他路由方法如下。
r.GET("/someGet",getting)
r.GET("/user/:name/*action",getParams) //此种方法,表示/usr路由下后可以跟其他任意匹配的路由信息。
r.POST("/somePost",posting)
r.PUT("/somePut",putting)
r.DELETE("/someDelete",deleting)∫
r.PATCH("/somePatch",patching)
r.HEAD("someHead",heading)
r.OPTIONS("/someOptions",options)
4,中间件的使用
4.1.在gin中的提供了中间件功能功能的使用,方便扩展代码扩展,使构建的服务更加完善。
gin中提供的New方法创建一个路由句柄,通过New方法创建的路由是不带中间件的。
如下代码:
package main
import (
"github.com/gin-gonic/gin"
"io"
"os"
)
//该方法模拟产生异常,用于服务中间件来进行捕获,并且开启一个文件,将日志写入文本,可查看异常的日志写入信息
func getFun(c *gin.Context) {
name := c.DefaultQuery("name","defaultName")
panic("test_panic")
c.String(200,"%v",name)
}
func main() {
/**
中间件的使用是实现服务运行过程所需要的一些特性方法
*/
//制定路由日志写入只对应的文本中
file, _:= os.Create("./gin.log")
//gin框架也可以指定写入的模块
gin.DefaultWriter = io.MultiWriter(file) //指定什么信息写入文本
gin.DefaultErrorWriter = io.MultiWriter(file) //固定错误也写入文本
r := gin.New() //创建一个无中间件的路由
r.Use(gin.Logger()) //使用日志中间件
r.Use(gin.Recovery()) //用于捕获程序的异常,防止程序出现挂掉的情况,将异常的信息会利用之前的插件写入到对应的文本中.
r.GET("/get",getFun)
r.Run()
}
4.2自定义中间件
因系统中提供的中间件可能满足不了服务的所需,所以需要实际使用中需要我们自定义相关的中间件,以满足服务所需的特性处理。
中间件自定义是一个闭包函数或者最定义规则类型的方法即可,使用Use方法将中间件注册只路由管理中,中间件定义方式如下:
package main
import "github.com/gin-gonic/gin"
//自定义中间见使用
//检查请求的ip是否在列表中,请求过来调用中间件函数进行处理
//自定义中间件的闭包格式,返回的是一个HandlerFunc类型的方法。
//func OutFun() gin.HandlerFunc {
// return Infunc(c *gin.Context) {
// //TODO
// }
//}
func IpAuthos() gin.HandlerFunc { //自定义中间件都是返回该函数指针
return func(c *gin.Context) {
ipList := []string{
"127.0.0.1",
}
flag := false
clientip := c.ClientIP()
for _, host := range ipList {
if clientip == host {
flag = true
break
}
}
if !flag {
c.String(500, "%s is not ipList", clientip)
c.Abort() //中断请求,中间件执行失败,终断
}
c.Next() //执行下一个中间件方法
}
}
func getFun(c *gin.Context) {
c.String(200, "success !!!")
}
func main() {
r := gin.Default()
r.Use(IpAuthos()) //使用自定义中间件
r.GET("/get", getFun)
r.Run()
}
4.4.在请求处理方法中使用协程
中间件或者处理方法中启动新协程不能使用原始的上下文,必须使用的是只读副本,具体示例如下:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"time"
)
func syncGet(cgp *gin.Context) {
time.Sleep(5 * time.Second)
cgp.String(200,"syncGet - %v",cgp.Request.URL.Path)
}
func main() {
r := gin.Default()
r.GET("/sget", func(c *gin.Context) {
time.Sleep(5 * time.Second)
fmt.Println("sycget - ",c.Request.URL.Path)
c.String(200,"sget - %v",c.Request.URL.Path)
})
r.GET("/sycget", func(c *gin.Context) {
//需要将主协程的上下文拷贝一份,在子协程中使用处理
cgp := c.Copy()
//服务函数重启一个协程来来处理,实现请求与返回异步处理
//界面收到处理结果,但实际后台还在处理,直到结果处理完成即可
go func() {
time.Sleep(5 * time.Second)
fmt.Println("sycget - ",cgp.Request.URL.Path)
//cgp.String(200,"sycget - %v",cgp.Request.URL.Path)
//cgp.JSON(200,gin.H{"message":cgp.Request.URL.Path})
}()
})
r.Run()
}
4.5,解析请求中的json并绑定至结构体。
通过接受的请求,将请求的json信息绑定只结构体中,以便后续的业务处理,如下示例:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
type myJson struct {
User string `json:"user"`
Passwd string `json:"passwd"`
}
//json
func getJson(c *gin.Context) {
req := myJson{}
//解析json绑定只结构
if err := c.ShouldBindJSON(&req);err == nil {
fmt.Println(req.User + req.Passwd)
if req.User == "lx" && req.Passwd == "llll" {
c.JSON(http.StatusOK,gin.H{"status":"you are logged in 2"})
} else {
c.JSON(http.StatusUnauthorized,gin.H{"status":"unauthorized 2"})
}
} else {
c.JSON(http.StatusBadRequest,gin.H{"error":err.Error()})
}
}
func main() {
r := gin.Default()
//json
r.POST("/loginJSON",getJson)
r.Run()
}
4.6.如何优雅的停止服务。
通常启动http服务,直接通过Run方法启动服务,如下示例不使用Run启动服务,并且模拟服务异常,优雅的停止服务
package main
import (
"context"
"fmt"
"github.com/gin-gonic/gin"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
r := gin.Default()
r.GET("/test",func(c *gin.Context){
time.Sleep(10 * time.Second)
c.String(200,"hello self server !!!")
})
//不再使用run启动服务,自定义服务启动
svr := &http.Server{
Addr: ":8089",
Handler: r,
}
//利用一个协程启动服务
go func(){
if err := svr.ListenAndServe();err != nil && err != http.ErrServerClosed {
log.Fatalf("listen %s is",err)
}
}()
quit := make(chan os.Signal)
signal.Notify(quit,syscall.SIGINT,syscall.SIGTERM) //捕获两个信号
<-quit //利用通道让程序阻塞
log.Fatalf("shutdown server ...")
//context.Background() //返回一个空根节点,用于处理后续生成的叶子结点而存在,无过期时间限制
//创建一个超时上下文
//context.WithTimeout() 返回一个ctx上下文跟一个控制取消函数
ctx, Cancle := context.WithTimeout(context.Background(),10 * time.Second)
defer Cancle()
if err := svr.Shutdown(ctx); err != nil {
log.Fatalf("server shudown %s",err.Error())
}
fmt.Println("server exiting")
}
另外通常使用的Run方法实质上内部调用也是httpserver服务,Run方法内部实现过程,示例代码:
func (engine *Engine) Run(addr ...string) (err error) {
defer func() { debugPrintError(err) }()
address := resolveAddress(addr)
debugPrint("Listening and serving HTTP on %s\n", address)
err = http.ListenAndServe(address, engine)
return
}
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
4.7,解析json只结构体的参数验证器使用:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
//使用标签验证字段绑定
type Person struct {
Age int `form:"age" binding:"required,gt=10"` //直接使用标签验证相关字段的可行性
Name string `form:"name" binding:"required"`
Address string `form:"address" binding:"required"`
}
func get(c *gin.Context) {
pe := Person{}
if err := c.ShouldBind(&pe);err != nil {
fmt.Println(err.Error())
c.String(500,"%v",err)
} else {
c.String(200,"%v",pe)
}
}
func main() {
r := gin.Default()
r.GET("/get",get)
r.Run()
}
如上示例代码,发放请求的参数不符合规则,会报错,提示参数验证不正确。
4.8,参数的获取Param(),Query(),PostForm()
如下代码示例子各种参数的获取方式:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
//Param
func getParams(c *gin.Context) {
name := c.Param("name") //路径name
action := c.Param("action") //操作
message := name + " is " + action
fmt.Println(name, action)
c.String(http.StatusOK, message)
}
//Query
一般匹配这种形式的url /welcome?firstname=Jane&lastname=Doe
func getWelcome(c *gin.Context) {
firstname := c.DefaultQuery("firstname", "Guest") //如果没有值,还可以给一个默认值
lastname := c.Query("lastname")
c.String(http.StatusOK, "Hello %s %s ", firstname, lastname)
}
//PostForm
//curl -H "Content-Type: application/x-www-form-urlencoded" -X POST "http://127.0.0.1:8080/form_post" -d 'message=asdfg&nick=clear'
//使用如上方法传递对应参数即可获取url数据表单
func getForm(c *gin.Context) {
//使用如下方法获取url参数,header一定要为x-www-form-urlencoded
message := c.PostForm("message")
nick := c.DefaultPostForm("nick","guest")
fmt.Println(message + nick)
c.JSON(200,gin.H{"status":"posted","message":message,"nick":nick})
}
func main() {
//query param
//一般匹配这种形式的url /welcome?firstname=Jane&lastname=Doe
r := gin.Default()
r.GET("/welcome", getWelcome)
r.GET("/getParam",getParams)
r.GET("/getForm",getForm)
r.Run()
}
4.9.匹配不定参数的路由
匹配格式:/路由/:参数名 or *参数名
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
router := gin.Default()
//路由后面匹配相关的参数
// 此 handler 将匹配 /user/john 但不会匹配 /user/ 或者 /user,会返回404错误
router.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
c.String(http.StatusOK, "Hello %s", name)
})
// 此 handler 将匹配 /user/john/ 和 /user/john/send
// 如果没有其他路由匹配 /user/john,它将重定向到 /user/john/
router.GET("/user/:name/*action", func(c *gin.Context) {
name := c.Param("name")
action := c.Param("action")
message := name + " is " + action
c.String(http.StatusOK, message)
})
router.Run()
}
4.10,读取数据并解析
通过接收路由请求,通过另外一方调用请求读取数据,解析并返回对应的数据信息。
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
router := gin.Default()
router.GET("/someDataFromReader", func(c *gin.Context) {
response, err := http.Get("https://www.tencent.com/zh-cn/about.html#about-con-1")
if err != nil || response.StatusCode != http.StatusOK {
c.Status(http.StatusServiceUnavailable)
return
}
reader := response.Body
contentLength := response.ContentLength
contentType := response.Header.Get("Content-Type")
extraHeaders := map[string]string{
"Content-Disposition": `attachment; filename="gopher.png"`,
}
c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)
})
router.Run()
}
5,gin框架核心代码剖析
//gin.New()创建路由代码:
func New() *Engine {
debugPrintWARNINGNew()
engine := &Engine{ //创建一个路由根处理对象,后续的处理皆使用该句柄。
RouterGroup: RouterGroup{
Handlers: nil, //中间件切片集合
basePath: "/",
root: true,
},
FuncMap: template.FuncMap{},
RedirectTrailingSlash: true,
RedirectFixedPath: false,
HandleMethodNotAllowed: false,
ForwardedByClientIP: true,
AppEngine: defaultAppEngine,
UseRawPath: false,
RemoveExtraSlash: false,
UnescapePathValues: true,
MaxMultipartMemory: defaultMultipartMemory,
trees: make(methodTrees, 0, 9), //trees 是最重要的点!!!!负责存储路由和handle方法的映射,采用类似字典树的结构
delims: render.Delims{Left: "{{", Right: "}}"},
secureJsonPrefix: "while(1);",
}
engine.RouterGroup.engine = engine
engine.pool.New = func() interface{} {
return engine.allocateContext()
}
return engine
}
//RouterGroup结构:
type RouterGroup struct {
Handlers HandlersChain //处理函数的切片集合
basePath string
engine *Engine
root bool
}
//实质上是函数类型的结合列表
type HandlerFunc func(*Context)
// HandlersChain defines a HandlerFunc array.
type HandlersChain []HandlerFunc
//创建路由时的流程代码:
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
absolutePath := group.calculateAbsolutePath(relativePath) //配合之前创建的router的设定的接触路径,得到当前方法路由的的绝对路径信息
handlers = group.combineHandlers(handlers) //重新编排处理函数的切片
group.engine.addRoute(httpMethod, absolutePath, handlers) //将该处理函数添加至路由处理函数数组中
return group.returnObj()
}
//添加路由方法
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
assert1(path[0] == '/', "path must begin with '/'")
assert1(method != "", "HTTP method can not be empty")
assert1(len(handlers) > 0, "there must be at least one handler")
debugPrintRoute(method, path, handlers)
root := engine.trees.get(method) //该处利用通过创建tree结构管理路由方法,并切一个路由名称匹配一个路由方法构成一个节点
if root == nil {
root = new(node)
root.fullPath = "/"
engine.trees = append(engine.trees, methodTree{method: method, root: root})
}
root.addRoute(path, handlers)
}
//如下为方法tree结构,以及根据方法获取路由树节点,并能从中获取对应的处理函数
type methodTree struct {
method string
root *node
}
type methodTrees []methodTree
func (trees methodTrees) get(method string) *node {
for _, tree := range trees {
if tree.method == method {
return tree.root
}
}
return nil
}
6.gin-参数验证器
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
)
// 标签绑定验证
// 标记集中对应的绑定类型,具体请求过来,可以通过对应的请求进行绑定
type Person struct {
User string `json:"user" form:"user" xml:"user" binding:"required"`
Passwd string `json:"passwd" form:"passwd" xml:"passwd" binding:"required" validate:"passwd"`
}
var (
validate = validator.New()
)
func init() {
if err := validate.RegisterValidation("passwd", validatePasswdFunc); err != nil {
fmt.Println("register validatePasswdFunc is failed")
}
}
// 自定义验证函数,设置validate 以及自定义tag标签
// validatePasswdFunc
func validatePasswdFunc(fl validator.FieldLevel) bool {
pass := fl.Field().String()
if len(pass) > 0 {
return true
}
return false
}
func main() {
r := gin.Default()
// bind JSON
r.POST("/loginJSON", func(c *gin.Context) {
pe := Person{}
if err := c.ShouldBindJSON(&pe); err != nil { // 绑定结构,会调用对应的验证函数验证参数
c.String(400, "Bind pe json is failed !")
}
if pe.User == "**" && pe.Passwd == "***" {
c.String(200, "Bind pe json is success !")
}
})
// bind XML
r.POST("/loginXML", func(c *gin.Context) {
pe := Person{}
if err := c.ShouldBindXML(&pe); err != nil {
fmt.Println("err = ", err.Error())
c.String(400, "Bind pe xml is failed !")
}
if pe.User == "***" && pe.Passwd == "***" {
c.String(200, "Bind pe xml is success !")
}
})
// bind HTML
r.POST("/loginHTML", func(c *gin.Context) {
pe := Person{}
// 此处利用Content-Type Header 推断使用哪个绑定器
if err := c.ShouldBind(&pe); err != nil {
c.String(400, "Bind pe html is failed !")
}
if pe.User == "***" && pe.Passwd == "***" {
c.String(200, "Bind pe html is success !")
}
})
r.Run(":8898")
}
如上结构中,制定对应的验证字段信息,在接受请求,validator自带的验证器会对设置了的规则的字段进行较验证,如果有错误,返回相关的信息。
7.自定义验证器
package main
import (
"fmt"
"github.com/go-playground/validator"
"github.com/satori/go.uuid"
"unicode/utf8"
)
type UserInfo struct {
Id string `validate:"uuid"` // UUID 类型
Name string `validate:"checkName"` // 自定义校验
Clear string `validate:"clear"`
Age uint8 `validate:"min=0,max=130"` // 0<=Age<=130
}
// 自定义校验函数
func checkName(f validator.FieldLevel) bool { // FieldLevel contains all the information and helper functions to validate a field
count := utf8.RuneCountInString(f.Field().String()) // 通过utf8编码,获取字符串长度
if count >= 2 && count <= 12 {
return true
}
return false
}
func main() {
validate := validator.New()
// 自定义函数checkName与 struct tag 关联起来
err := validate.RegisterValidation("checkName", checkName)
if err != nil {
fmt.Println("注册失败!")
return
}
err = validate.RegisterValidation("clear", checkName)
if err != nil {
fmt.Println("注册失败!")
return
}
// UUID引用:satori/go.uuid;添加方法:go get github.com/satori/go.uuid
uuid := uuid.Must(uuid.NewV1(), err) // 通过satori/go.uuid,快速生成UUID
fmt.Println("UUID:", uuid)
user := UserInfo{
Id: uuid.String(),
Name: "jiangzhou",
Clear: "c",
Age: 223,
}
err = validate.Struct(user)
if err != nil {
for _, e := range err.(validator.ValidationErrors) {
fmt.Println("错误字段:", e.Field())
fmt.Println("错误值:", e.Value())
fmt.Println("错误tag:", e.Tag())
}
return
}
fmt.Println("校验成功")
}
8 swag用法
1.介绍
swag是一个golang通过在代码中对相关的接口方法进行注释标记,通过swag工具生成对应可通过web访问的api文档的一个可视化工具。
2.安装
[root@localhost]# go get -u github.com/swaggo/swag/cmd/swag
[root@localhost]# go get -u github.com/swaggo/gin-swagger
[root@localhost]# go get -u github.com/swaggo/gin-swagger/swaggerFiles
安装如上的三个文件包,稍后通过如下命令检查swag是否安装成功。
[root@localhost ~]# swag -v
swag version v1.6.7
如果安装完之后,发现没找swag命令,需要配置配置环境变量
mv $GOPATH/bin/swag /usr/local/go/bin
export PATH=$PATH:/usr/local/go/bin
在进行测试即可,表明swag安装成功。
3.使用示范
注视格式:
示例1:
@Summary 是对该接口的一个摘要
@Description 是对该接口的一个描述
@Id 是一个全局标识符,所有的接口文档中 Id 不能标注
@Tags 是对接口的标注,同一个 tag 为一组,这样方便我们整理接口
@Version 表明该接口的版本
@Accept 表示该该请求的请求类型
@Produce 表示该该请求的返回类型
@Param 表示参数 分别有以下参数 参数名词 参数类型 数据类型 是否必须 注释 属性(可选参数),参数之间用空格隔开。
@Success 表示请求成功后返回,它有以下参数 请求返回状态码,参数类型,数据类型,注释
@Failure 请求失败后返回,参数同上
@Router 该函数定义了请求路由并且包含路由的请求方式。
for example:
// GetReportData 前端获取上报数据
// @Summary 前端获取上报数据
// @Tags 数据上报
// @Accept json
// @Produce json
// @Param Request body report.StWSGetReportDataReq true " "
// @Success 200 {object} report.StWSGetReportDataRsp
// @Router /api/Report/GetReportData [POST]
func GetReportData(c *gin.Context) {
}
说明:
上图有一个错误:
// @router / [Post] 下面不应该有空行
// @Title Get Product list
// @Description Get Product list by some info
// @Success 200 {object} models.ZDTProduct.ProductList
// @Param category_id query int false “category id”
// @Param brand_id query int false “brand id”
// @Param query query string false “query of search”
// @Param segment query string false “segment”
// @Param sort query string false “sort option”
// @Param dir query string false “direction asc or desc”
// @Param offset query int false “offset”
// @Param limit query int false “count limit”
// @Param price query float false “price”
// @Param special_price query bool false “whether this is special price”
// @Param size query string false “size filter”
// @Param color query string false “color filter”
// @Param format query bool false “choose return format”
// @Failure 400 no enough input
// @Failure 500 get products common error
// @router /products [get]
func (c *CMSController) Product() {
}
首先是 CMSController 定义上面的注释,这个是用来显示这个模块的作用。接下来就是每一个函数上面的注释,这里列出来支持的各种注释:
@Title
这个 API 所表达的含义,是一个文本,空格之后的内容全部解析为 title
@Description
这个 API 详细的描述,是一个文本,空格之后的内容全部解析为 Description
@Param
参数,表示需要传递到服务器端的参数,有五列参数,使用空格或者 tab 分割,五个分别表示的含义如下
1.参数名
2.参数类型,可以有的值是 formData、query、path、body、header,formData 表示是 post 请求的数据,query 表示带在 url 之后的参数,path 表示请求路径上得参数,例如上面例子里面的 key,body 表示是一个 raw 数据请求,header 表示带在 header 信息中得参数。
3.参数类型
4.是否必须
5.注释
@Success
成功返回给客户端的信息,三个参数,第一个是 status code。第二个参数是返回的类型,必须使用 {} 包含,第三个是返回的对象或者字符串信息,如果是 {object} 类型,那么 bee 工具在生成 docs 的时候会扫描对应的对象,这里填写的是想对你项目的目录名和对象,例如 models.ZDTProduct.ProductList 就表示 /models/ZDTProduct 目录下的 ProductList 对象。
三个参数必须通过空格分隔
Failure
失败返回的信息,包含两个参数,使用空格分隔,第一个表示 status code,第二个表示错误信息
@router
路由信息,包含两个参数,使用空格分隔,第一个是请求的路由地址,支持正则和自定义路由,和之前的路由规则一样,第二个参数是支持的请求方法,放在 [] 之中,如果有多个方法,那么使用 , 分隔。
代码示例:
package main
import (
_ "GoFream/goOther/goMySwag/docs"
"github.com/gin-gonic/gin"
ginSwagger "github.com/swaggo/gin-swagger"
"github.com/swaggo/gin-swagger/swaggerFiles"
"net/http"
)
// @title 测试
// @version 0.0.1
// @description 测试
// @BasePath
func main() {
//dir, _ := os.Getwd()
//fmt.Println("当前路径:", dir)
r := gin.New()
// 文档界面访问URL
// http://127.0.0.1:8980/swagger/index.html
//启动创建swagapi文档路由方式
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
// 创建路由组
v1 := r.Group("/api/v1/")
{
v1.GET("/record/:userId", record)
v1.GET("/sayHello/:name", sayHello)
v1.POST("/clsPost/:addr", clsPost)
}
r.Run(":8980")
}
// @获取指定ID记录
// @Description get record by ID
// @Accept json
// @Produce json
// @Param some_id path int true "userId"
// @Success 200 {string} string "ok"
// @Router /api/v1/record/{some_id} [get]
func record(c *gin.Context) {
c.String(http.StatusOK, "ok")
}
// @你好世界
// @Description 你好
// @Accept json
// @Produce json
// @Param name path string true "name"
// @Success 200 {string} string "name,helloWorld"
// @Router /api/v1/sayHello/{name} [get]
func sayHello(c *gin.Context) {
name := c.Param("name")
c.String(http.StatusOK, name+",helloWorld")
}
//@This is Post request
//@Accept json
//@Produce json
//@Param name path string true "clspost"
//@Success 200 {string} string
//@Router /api/v1/clsPost/{name} [post]
func clsPost(c *gin.Context) {
name := c.Param("clsPost")
//c.JSON(200, gin.H{"msg": name})
c.String(200, name)
}
在代码工作目录下执行如下命令:
swag init
无报错,表示生成文档api成功.
4.访问文档api
启动对应服务,并通过http://127.0.0.1:8980/swagger/index.html 访问api文档服务,如下图所示:
如上三种接口方法,可以通过api结构测试服务接口方法相关信息: