Gin学习笔记

1.gin框架图解概览

Gin学习笔记_第1张图片

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) {
}

说明:

Gin学习笔记_第2张图片

 

上图有一个错误:
// @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文档服务,如下图所示:

Gin学习笔记_第3张图片 如上三种接口方法,可以通过api结构测试服务接口方法相关信息:

Gin学习笔记_第4张图片

 

 

你可能感兴趣的:(golang笔记,gin,学习)