我是靠谱客的博主 平淡酒窝,最近开发中收集的这篇文章主要介绍gin框架相关汇总报告gin框架的相关笔记(各方汇总,抄袭成瘾),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

gin框架的相关笔记(各方汇总,抄袭成瘾)

1.gin框架的介绍

1.1官方文档地址:https://gin-gonic.com/docs/

1.2官网介绍

Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API, but with performance up to 40 times faster than Martini. If you need smashing performance, get yourself some Gin.

安装:

 go get -u github.com/gin-gonic/gin

1.3 官网例子

package main

import "github.com/gin-gonic/gin"

func main() {
	r := gin.Default()
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})
	r.Run() // listen and serve on 0.0.0.0:8080
}

介绍

  • Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确,具有快速灵活,容错方便等特点
  • 对于golang而言,web框架的依赖要远比Python,Java之类的要小。自身的net/http足够简单,性能也非常不错
  • 借助框架开发,不仅可以省去很多常用的封装带来的时间,也有助于团队的编码风格和形成规范

helloword例子

package main

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

func main() {
    // 1.创建路由
   r := gin.Default()
   // 2.绑定路由规则,执行的函数
   // gin.Context,封装了request和response
   r.GET("/", func(c *gin.Context) {
      c.String(http.StatusOK, "hello World!")
   })
   // 3.监听端口,默认在8080
   // Run("里面不指定端口号默认为8080") 
   r.Run(":8000")
}

2.gin数据解析和绑定

it行业处理业务,就是把现实中的业务转化为数据处理的过程,这期间不仅仅涉及技术实力,还涉及对业务的转化能力,也就是所谓业务能力,但技术是关键,很多时候去lettcode的算法题,总感觉很简单,就是写不出来,你说气人不气人。所以学web框架最先要了解的我认为也是数据处理能力,这个才是首要的。

2.1 json数据解析和绑定

一个小demo

package main

import (
   "github.com/gin-gonic/gin"
   "net/http"
)

// 定义接收数据的结构体
type Login struct {
   // binding:"required"修饰的字段,若接收为空值,则报错,是必须字段
   User    string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"`
   Pssword string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"`
}

func main() {
   // 1.创建路由
   // 默认使用了2个中间件Logger(), Recovery()
   r := gin.Default()
   // JSON绑定
   r.POST("loginJSON", func(c *gin.Context) {
      // 声明接收的变量
      var json Login
      // 将request的body中的数据,自动按照json格式解析到结构体
      if err := c.ShouldBindJSON(&json); err != nil {
         // 返回错误信息
         // gin.H封装了生成json数据的工具
         c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
         return
      }
      // 判断用户名密码是否正确
      if json.User != "root" || json.Pssword != "admin" {
         c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
         return
      }
      c.JSON(http.StatusOK, gin.H{"status": "200"})
   })
   r.Run(":8000")
}

用postman请求返回的结果如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DmSNUU4X-1629458514219)(D:笔记golang相关笔记imagesginbind.png)]

由这个简单的示例可以知道要绑定json数据可以采用ShouldBindJSON这个方法。

其实很多方法都可以解析json数据,Bind(),BindJson(),ShoudBindWith()等都可以,

Bind()方法是全能的,可以自动判断 Content-Type MIME of the most common data formats.源码中很多这样的定义,很多可以简写。根据Content-Type的不同调用不同的封装。但是Bind()是万能的解析,因为所有其他的方法都封装子Bind(),但Bind()默认解析form格式,可以自行修改测试。

2.2 URI数据解析和绑定

栗子:

package main

import (
   "fmt"
   "net/http"

   "github.com/gin-gonic/gin"
)

// 定义接收数据的结构体
type Login1 struct {
   // binding:"required"修饰的字段,若接收为空值,则报错,是必须字段
   User    string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"`
   Pssword string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"`
}

func main() {
   // 1.创建路由
   // 默认使用了2个中间件Logger(), Recovery()
   r := gin.Default()
   // JSON绑定
   r.GET("/:user/:password", func(c *gin.Context) {
      // 声明接收的变量
      var login Login1
      // Bind()默认解析并绑定form格式
      // 根据请求头中content-type自动推断
      if err := c.ShouldBindUri(&login); err != nil {
         c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
         return
      }
      fmt.Println(login)
      // 判断用户名密码是否正确
      if login.User != "root" || login.Pssword != "admin" {
         c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
         return
      }
      c.JSON(http.StatusOK, gin.H{"status": "200"})
   })
   r.Run(":8000")
}

这个采用的是ShouldBindUri(),postman请求被结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rCyJNaBN-1629458514222)(D:笔记golang相关笔记imagesginbinduri.png)]

2.3 单独解析某个参数传入

package main

import (
   "fmt"
   "github.com/gin-gonic/gin"
)

func  main(){
   engine:=gin.Default()
   //http://localhost:8080/hello?name=davie
   engine.GET("/hello", func(c *gin.Context) {
      fmt.Println(c.FullPath())
      name:=c.Query("name")
      fmt.Println(name)

      c.Writer.Write([]byte("hello,"+name))

   })


   //http://localhost:8080/login
   /*
      在Postman软件中,设置访问方式为POST,网址为http://localhost:8080/login;在Body中选择x-www-form-urlencoded
      并填入:
      key:username  value:davie
      key:password  value:123
   */
   engine.POST("/login", func(c *gin.Context) {
      fmt.Println(c.FullPath())
      username,exist:=c.GetPostForm("username")
      if exist{
         fmt.Println(username)
      }

      password,exist:=c.GetPostForm("password")
      if exist{
         fmt.Println(password)
      }

      c.Writer.Write([]byte(username+"登录"))
   })

   //删除的是某个用户的值,:id代表的是变量
   engine.DELETE("/user/:id", func(c *gin.Context) {
      userID:=c.Param("id")
      fmt.Println(userID)
      c.Writer.Write([]byte("delete 用户ID:"+userID))
   })

   engine.Run()
}

这段代码中Query()方法或其最终的一系列方法解析?k=v的数据

可用GetPostForm()方法解析form表单的数据只不过是一个一个获取,前面我们说过用Bind()方法获取,那种是获取所有并绑定在结构体上。

可用Param()方法获取uri的信息,同样是区别Bind()绑定。

3. gin框架渲染数据

3.1各种数据格式的渲染

package main

import (
   "github.com/gin-gonic/gin"
   "github.com/gin-gonic/gin/testdata/protoexample"
)

// 多种响应方式
func main() {
   // 1.创建路由
   // 默认使用了2个中间件Logger(), Recovery()
   r := gin.Default()
   // 1.json
   r.GET("/someJSON", func(c *gin.Context) {
      c.JSON(200, gin.H{"message": "someJSON", "status": 200})
   })
   // 2. 结构体响应
   r.GET("/someStruct", func(c *gin.Context) {
      var msg struct {
         Name    string
         Message string
         Number  int
      }
      msg.Name = "root"
      msg.Message = "message"
      msg.Number = 123
      c.JSON(200, msg)
   })
   // 3.XML
   r.GET("/someXML", func(c *gin.Context) {
      c.XML(200, gin.H{"message": "abc"})
   })
   // 4.YAML响应
   r.GET("/someYAML", func(c *gin.Context) {
      c.YAML(200, gin.H{"name": "zhangsan"})
   })
   // 5.protobuf格式,谷歌开发的高效存储读取的工具
   // 数组?切片?如果自己构建一个传输格式,应该是什么格式?
   r.GET("/someProtoBuf", func(c *gin.Context) {
      reps := []int64{int64(1), int64(2)}
      // 定义数据
      label := "label"
      // 传protobuf格式数据
      data := &protoexample.Test{
         Label: &label,
         Reps:  reps,
      }
      c.ProtoBuf(200, data)
   })

   r.Run(":8000")
}

首先是json数据的数据渲染

可以使用Json()方法,这个方法源码如下:

// JSON serializes the given struct as JSON into the response body.
// It also sets the Content-Type as "application/json".
// 将给定的结构作为 JSON 序列化到响应正文中。它还将 Content-Type 设置为“application/json”。
func (c *Context) JSON(code int, obj interface{}) {
   c.Render(code, render.JSON{Data: obj})
}

当然gin里面有很多方法可以旋绕为json,如AsciiJSON(),如官方源码就这样用过

package main

import (
   "github.com/gin-gonic/gin"
)
// 探究gin框架中数据解析和绑定
func main() {
   r := gin.Default()

   r.GET("/someJSON", func(c *gin.Context) {
      data := map[string]interface{}{
       "lang": "GO语言",
       "tag":  "<br>",
      }
      //c.JSON(200,gin.H{"lang": "GO语言",
            "tag":  "<br>",})
      // will output : {"lang":"GOu8bedu8a00","tag":"u003cbru003e"}
      c.AsciiJSON(http.StatusOK, data)
   })

   // Listen and serve on 0.0.0.0:8080
   r.Run(":8080")
}

AsciiJSON()的源码

// AsciiJSON serializes the given struct as JSON into the response body with unicode to ASCII string.
// It also sets the Content-Type as "application/json".
// 将给定的结构作为 JSON 序列化到带有 Unicode 到 ASCII 字符串的响应正文中。它还将 Content-Type 设置为“applicationjson”。
func (c *Context) AsciiJSON(code int, obj interface{}) {
   c.Render(code, render.AsciiJSON{Data: obj})
}

当然我们这里只列举两个,源码中有很多,gin的源码还是很清晰。

其他的方法就不过多的介绍了,这段代码中列举的清清楚楚。

3.2HTML的渲染

  • gin支持加载HTML模板, 然后根据模板参数进行配置并返回相应的数据,本质上就是字符串替换
  • LoadHTMLGlob()方法可以加载模板文件
package main

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()
    r.LoadHTMLGlob("tem/*")
    r.GET("/index", func(c *gin.Context) {
        c.HTML(http.StatusOK, "index.html", gin.H{"title": "我是测试", "ce": "123456"})
    })
    r.Run()
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>{{.title}}</title>
</head>
    <body>
        fgkjdskjdsh{{.ce}}
    </body>
</html>

目录结构:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QpuMpyAq-1629458514224)(D:笔记golang相关笔记imagesmulu.png)]

在goland运行可能会报一个bug。如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NpI9Dhj8-1629458514226)(D:笔记golang相关笔记imagesgolandgin.png)]

可以参考https://blog.csdn.net/hezhongla0811/article/details/105719383这篇博客有详细讲解。

总之要玩的很溜还是知道标准库中template的具体用法。这里不过叙述了。可以参考http://www.topgoer.com/gin%E6%A1%86%E6%9E%B6/gin%E6%B8%B2%E6%9F%93/html%E6%A8%A1%E6%9D%BF%E6%B8%B2%E6%9F%93.html

重定向

Redirect()方法实现重定向。

源码:

// Redirect returns a HTTP redirect to the specific location.
func (c *Context) Redirect(code int, location string) {
   c.Render(-1, render.Redirect{
      Code:     code,
      Location: location,
      Request:  c.Request,
   })
}

3.3同步异步

  • goroutine机制可以方便地实现异步处理
  • 另外,在启动新的goroutine时,不应该使用原始上下文,必须使用它的只读副本
package main

import (
    "log"
    "time"

    "github.com/gin-gonic/gin"
)

func main() {
    // 1.创建路由
    // 默认使用了2个中间件Logger(), Recovery()
    r := gin.Default()
    // 1.异步
    r.GET("/long_async", func(c *gin.Context) {
        // 需要搞一个副本
        copyContext := c.Copy()
        // 异步处理
        go func() {
            time.Sleep(3 * time.Second)
            log.Println("异步执行:" + copyContext.Request.URL.Path)
        }()
    })
    // 2.同步
    r.GET("/long_sync", func(c *gin.Context) {
        time.Sleep(3 * time.Second)
        log.Println("同步执行:" + c.Request.URL.Path)
    })

    r.Run(":8000")
}

4.文件上传及路由

4.1 上传单个文件

  • multipart/form-data格式用于文件上传
  • gin文件上传与原生的net/http方法类似,不同在于gin把原生的request封装到c.Request中
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data">
          上传文件:<input type="file" name="file" >
          <input type="submit" value="提交">
    </form>
</body>
</html>
package main

import (
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()
    //限制上传最大尺寸
    r.MaxMultipartMemory = 8 << 20
    r.POST("/upload", func(c *gin.Context) {
        file, err := c.FormFile("file")
        if err != nil {
            c.String(500, "上传图片出错")
        }
        // c.JSON(200, gin.H{"message": file.Header.Context})
        c.SaveUploadedFile(file, file.Filename)
        c.String(http.StatusOK, file.Filename)
    })
    r.Run()
}
4.1.1 上传特定文件

有的用户上传文件需要限制上传文件的类型以及上传文件的大小,但是gin框架暂时没有这些函数,因此基于原生的函数写法自己写了一个可以限制大小以及文件类型的上传函数

package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()
    r.POST("/upload", func(c *gin.Context) {
        _, headers, err := c.Request.FormFile("file")
        if err != nil {
            log.Printf("Error when try to get file: %v", err)
        }
        //headers.Size 获取文件大小
        if headers.Size > 1024*1024*2 {
            fmt.Println("文件太大了")
            return
        }
        //headers.Header.Get("Content-Type")获取上传文件的类型
        if headers.Header.Get("Content-Type") != "image/png" {
            fmt.Println("只允许上传png图片")
            return
        }
        c.SaveUploadedFile(headers, "./video/"+headers.Filename)
        c.String(http.StatusOK, headers.Filename)
    })
    r.Run()
}

也可以参考这篇博文算是比较详细的了,

https://blog.csdn.net/kuangshp128/article/details/109598786

4.2 上传多个文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <form action="http://localhost:8000/upload" method="post" enctype="multipart/form-data">
          上传文件:<input type="file" name="files" multiple>
          <input type="submit" value="提交">
    </form>
</body>
</html>
package main

import (
   "github.com/gin-gonic/gin"
   "net/http"
   "fmt"
)

// gin的helloWorld

func main() {
   // 1.创建路由
   // 默认使用了2个中间件Logger(), Recovery()
   r := gin.Default()
   // 限制表单上传大小 8MB,默认为32MB
   r.MaxMultipartMemory = 8 << 20
   r.POST("/upload", func(c *gin.Context) {
      form, err := c.MultipartForm()
      if err != nil {
         c.String(http.StatusBadRequest, fmt.Sprintf("get err %s", err.Error()))
      }
      // 获取所有图片
      files := form.File["files"]
      // 遍历所有图片
      for _, file := range files {
         // 逐个存
         if err := c.SaveUploadedFile(file, file.Filename); err != nil {
            c.String(http.StatusBadRequest, fmt.Sprintf("upload err %s", err.Error()))
            return
         }
      }
      c.String(200, fmt.Sprintf("upload ok %d files", len(files)))
   })
   //默认端口号是8080
   r.Run(":8000")
}

当然也可以看那篇博客比较详细,这里只是换了一个接收方法MultipartForm()

4.3 routes group

  • routes group是为了管理一些相同的URL
package main

import (
   "github.com/gin-gonic/gin"
   "fmt"
)

// gin的helloWorld

func main() {
   // 1.创建路由
   // 默认使用了2个中间件Logger(), Recovery()
   r := gin.Default()
   // 路由组1 ,处理GET请求
   v1 := r.Group("/v1")
   // {} 是书写规范
   {
      v1.GET("/login", login)
      v1.GET("submit", submit)
   }
   v2 := r.Group("/v2")
   {
      v2.POST("/login", login)
      v2.POST("/submit", submit)
   }
   r.Run(":8000")
}

func login(c *gin.Context) {
   name := c.DefaultQuery("name", "jack")
   c.String(200, fmt.Sprintf("hello %sn", name))
}

func submit(c *gin.Context) {
   name := c.DefaultQuery("name", "lily")
   c.String(200, fmt.Sprintf("hello %sn", name))
}

4.4 路由拆分与注册

1.1.1. 基本的路由注册

下面最基础的gin路由注册方式,适用于路由条目比较少的简单项目或者项目demo。

package main

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

func helloHandler(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{
        "message": "Hello www.topgoer.com!",
    })
}

func main() {
    r := gin.Default()
    r.GET("/topgoer", helloHandler)
    if err := r.Run(); err != nil {
        fmt.Println("startup service failed, err:%vn", err)
    }
}
1.1.2. 路由拆分成单独文件或包

当项目的规模增大后就不太适合继续在项目的main.go文件中去实现路由注册相关逻辑了,我们会倾向于把路由部分的代码都拆分出来,形成一个单独的文件或包:

我们在routers.go文件中定义并注册路由信息:

package main

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

func helloHandler(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{
        "message": "Hello www.topgoer.com!",
    })
}

func setupRouter() *gin.Engine {
    r := gin.Default()
    r.GET("/topgoer", helloHandler)
    return r
}

此时main.go中调用上面定义好的setupRouter函数:

func main() {
    r := setupRouter()
    if err := r.Run(); err != nil {
        fmt.Println("startup service failed, err:%vn", err)
    }
}

此时的目录结构:

gin_demo
├── go.mod
├── go.sum
├── main.go
└── routers.go

把路由部分的代码单独拆分成包的话也是可以的,拆分后的目录结构如下:

gin_demo
├── go.mod
├── go.sum
├── main.go
└── routers
    └── routers.go

routers/routers.go需要注意此时setupRouter需要改成首字母大写:

package routers

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

func helloHandler(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{
        "message": "Hello www.topgoer.com",
    })
}

// SetupRouter 配置路由信息
func SetupRouter() *gin.Engine {
    r := gin.Default()
    r.GET("/topgoer", helloHandler)
    return r
}

main.go文件内容如下:

package main

import (
    "fmt"
    "gin_demo/routers"
)

func main() {
    r := routers.SetupRouter()
    if err := r.Run(); err != nil {
        fmt.Println("startup service failed, err:%vn", err)
    }
}
1.1.3. 路由拆分成多个文件

当我们的业务规模继续膨胀,单独的一个routers文件或包已经满足不了我们的需求了,

func SetupRouter() *gin.Engine {
    r := gin.Default()
    r.GET("/topgoer", helloHandler)
    r.GET("/xx1", xxHandler1)
    ...
    r.GET("/xx30", xxHandler30)
    return r
}

因为我们把所有的路由注册都写在一个SetupRouter函数中的话就会太复杂了。

我们可以分开定义多个路由文件,例如:

gin_demo
├── go.mod
├── go.sum
├── main.go
└── routers
    ├── blog.go
    └── shop.go

routers/shop.go中添加一个LoadShop的函数,将shop相关的路由注册到指定的路由器:

func LoadShop(e *gin.Engine)  {
    e.GET("/hello", helloHandler)
    e.GET("/goods", goodsHandler)
    e.GET("/checkout", checkoutHandler)
    ...
}

routers/blog.go中添加一个LoadBlog的函数,将blog相关的路由注册到指定的路由器:

func LoadBlog(e *gin.Engine) {
    e.GET("/post", postHandler)
    e.GET("/comment", commentHandler)
    ...
}

在main函数中实现最终的注册逻辑如下:

func main() {
    r := gin.Default()
    routers.LoadBlog(r)
    routers.LoadShop(r)
    if err := r.Run(); err != nil {
        fmt.Println("startup service failed, err:%vn", err)
    }
}
1.1.4. 路由拆分到不同的APP

有时候项目规模实在太大,那么我们就更倾向于把业务拆分的更详细一些,例如把不同的业务代码拆分成不同的APP。

因此我们在项目目录下单独定义一个app目录,用来存放我们不同业务线的代码文件,这样就很容易进行横向扩展。大致目录结构如下:

gin_demo
├── app
│   ├── blog
│   │   ├── handler.go
│   │   └── router.go
│   └── shop
│       ├── handler.go
│       └── router.go
├── go.mod
├── go.sum
├── main.go
└── routers
    └── routers.go

其中app/blog/router.go用来定义post相关路由信息,具体内容如下:

func Routers(e *gin.Engine) {
    e.GET("/post", postHandler)
    e.GET("/comment", commentHandler)
}

app/shop/router.go用来定义shop相关路由信息,具体内容如下:

func Routers(e *gin.Engine) {
    e.GET("/goods", goodsHandler)
    e.GET("/checkout", checkoutHandler)
}

routers/routers.go中根据需要定义Include函数用来注册子app中定义的路由,Init函数用来进行路由的初始化操作:

type Option func(*gin.Engine)

var options = []Option{}

// 注册app的路由配置
func Include(opts ...Option) {
    options = append(options, opts...)
}

// 初始化
func Init() *gin.Engine {
    r := gin.New()
    for _, opt := range options {
        opt(r)
    }
    return r
}

main.go中按如下方式先注册子app中的路由,然后再进行路由的初始化:

func main() {
    // 加载多个APP的路由配置
    routers.Include(shop.Routers, blog.Routers)
    // 初始化路由
    r := routers.Init()
    if err := r.Run(); err != nil {
        fmt.Println("startup service failed, err:%vn", err)
    }
}

5.中间件(摘自李文周的博客)

Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。

定义中间件

Gin中的中间件必须是一个gin.HandlerFunc类型。例如我们像下面的代码一样定义一个统计请求耗时的中间件。

// StatCost 是一个统计耗时请求耗时的中间件
func StatCost() gin.HandlerFunc {
	return func(c *gin.Context) {
		start := time.Now()
		c.Set("name", "小王子") // 可以通过c.Set在请求上下文中设置值,后续的处理函数能够取到该值
		// 调用该请求的剩余处理程序
		c.Next()
		// 不调用该请求的剩余处理程序
		// c.Abort()
		// 计算耗时
		cost := time.Since(start)
		log.Println(cost)
	}
}

注册中间件

在gin框架中,我们可以为每个路由添加任意数量的中间件。

为全局路由注册
func main() {
	// 新建一个没有任何默认中间件的路由
	r := gin.New()
	// 注册一个全局中间件
	r.Use(StatCost())
	
	r.GET("/test", func(c *gin.Context) {
		name := c.MustGet("name").(string) // 从上下文取值
		log.Println(name)
		c.JSON(http.StatusOK, gin.H{
			"message": "Hello world!",
		})
	})
	r.Run()
}
为某个路由单独注册
// 给/test2路由单独注册中间件(可注册多个)
	r.GET("/test2", StatCost(), func(c *gin.Context) {
		name := c.MustGet("name").(string) // 从上下文取值
		log.Println(name)
		c.JSON(http.StatusOK, gin.H{
			"message": "Hello world!",
		})
	})
为路由组注册中间件

为路由组注册中间件有以下两种写法。

写法1:

shopGroup := r.Group("/shop", StatCost())
{
    shopGroup.GET("/index", func(c *gin.Context) {...})
    ...
}

写法2:

shopGroup := r.Group("/shop")
shopGroup.Use(StatCost())
{
    shopGroup.GET("/index", func(c *gin.Context) {...})
    ...
}

中间件注意事项

gin默认中间件

gin.Default()默认使用了LoggerRecovery中间件,其中:

  • Logger中间件将日志写入gin.DefaultWriter,即使配置了GIN_MODE=release
  • Recovery中间件会recover任何panic。如果有panic的话,会写入500响应码。

如果不想使用上面两个默认的中间件,可以使用gin.New()新建一个没有任何默认中间件的路由。

gin中间件中使用goroutine

当在中间件或handler中启动新的goroutine时,不能使用原始的上下文(c *gin.Context),必须使用其只读副本(c.Copy())。

也可以参考这篇博客https://blog.csdn.net/qq_35709559/article/details/109609174

或者这篇https://blog.csdn.net/weixin_36162966/article/details/91383006

6.运行多个服务

我们可以在多个端口启动服务,例如:

package main

import (
	"log"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"golang.org/x/sync/errgroup"
)

var (
	g errgroup.Group
)

func router01() http.Handler {
	e := gin.New()
	e.Use(gin.Recovery())
	e.GET("/", func(c *gin.Context) {
		c.JSON(
			http.StatusOK,
			gin.H{
				"code":  http.StatusOK,
				"error": "Welcome server 01",
			},
		)
	})

	return e
}

func router02() http.Handler {
	e := gin.New()
	e.Use(gin.Recovery())
	e.GET("/", func(c *gin.Context) {
		c.JSON(
			http.StatusOK,
			gin.H{
				"code":  http.StatusOK,
				"error": "Welcome server 02",
			},
		)
	})

	return e
}

func main() {
	server01 := &http.Server{
		Addr:         ":8080",
		Handler:      router01(),
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
	}

	server02 := &http.Server{
		Addr:         ":8081",
		Handler:      router02(),
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
	}
   // 借助errgroup.Group或者自行开启两个goroutine分别启动两个服务
	g.Go(func() error {
		return server01.ListenAndServe()
	})

	g.Go(func() error {
		return server02.ListenAndServe()
	})

	if err := g.Wait(); err != nil {
		log.Fatal(err)
	}
}

案例:https://github.com/EDDYCJY/go-gin-example

对上面那个项目讲解的:https://golangroadmap.com/books/gin/1.html#%E4%B8%80%E3%80%81%E5%AE%89%E8%A3%85golang

最后

以上就是平淡酒窝为你收集整理的gin框架相关汇总报告gin框架的相关笔记(各方汇总,抄袭成瘾)的全部内容,希望文章能够帮你解决gin框架相关汇总报告gin框架的相关笔记(各方汇总,抄袭成瘾)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(28)

评论列表共有 0 条评论

立即
投稿
返回
顶部