写点什么

gin 框架上手实践

作者:FunTester
  • 2024-07-26
    河北
  • 本文字数:4853 字

    阅读完需:约 16 分钟

Gin框架是一个用 Go 语言编写的高性能 Web 框架,以其速度和简洁性著称。它由一个轻量级的 HTTP 路由器和一个中间件架构组成,能够处理大型流量并简化开发者的工作。Gin的主要特点包括内置的路由组、简洁的 API 设计、强大的错误处理机制、支持多种格式的请求绑定和验证,以及内置的日志记录功能。由于其性能优越和易于使用,Gin广泛应用于构建RESTful API和 Web 服务。其设计理念是尽可能减少繁琐的配置和代码,让开发者专注于业务逻辑,实现快速开发和部署。


PS:据部分资料显示,HTTP路由的优化,速度提升了 10 倍以上。但我是查到的主流资料,性能提升没这么夸张。对比我知道的一些框架,QPS 提升还可以,大多数都是小于 3 倍的,但是内存消耗非常小,内存分配和分配时间非常小。主要原因也是因为 gin 采用了大量的 sync.Pool 优化,这一点跟 fasthttp 一样的逻辑。所以池化技术是一个性能优化的大杀器。


下面我们来开始 gin 框架的实践之旅。

依赖和安装

  1. Go 语言环境

  2. 安装 gin :go get -u github.com/gin-gonic/gin

  3. 开始撸代码

这是一个例子

首先我们先看一个最简单的例子:


package main    import (      "github.com/gin-gonic/gin"      "net/http")    func main() {      //创建一个默认的路由引擎,默认使用了Logger和Recovery中间件      r := gin.Default()      //注册一个路由和处理函数      r.GET("/", func(c *gin.Context) {         //返回一个字符串,状态码是200         c.String(http.StatusOK, "Hello FunTester")      })      //监听端口,默认是8080,这里一般不设置IP,原因是在服务器上可能有多个IP,如果设置了IP,就只能监听这个IP      r.Run(":8000")  }
复制代码

请求与响应

上面的每行代码的功能都已经写好了注释,是不是非常简单,其中 GET 方法的参数类型是: handlers ...HandlerFunc ,而 HandlerFunc 的定义是 type HandlerFunc func(*Context) ,其中 gin.ContextGin 框架中最核心的结构之一,它提供了上下文环境,供处理 HTTP 请求和响应。gin.Context包含许多有用的方法和属性,使开发者能够轻松访问请求数据、设置响应数据、处理错误以及在中间件和处理器之间传递信息。


下面是 gin.Context 构造方法:


type Context struct {  writermem responseWriter  Request   *http.Request  Writer    ResponseWriter
Params Params handlers HandlersChain index int8 fullPath string
engine *Engine params *Params skippedNodes *[]skippedNode
// This mutex protects Keys map. mu sync.RWMutex
// Keys is a key/value pair exclusively for the context of each request. Keys map[string]any
// Errors is a list of errors attached to all the handlers/middlewares who used this context. Errors errorMsgs
// Accepted defines a list of manually accepted formats for content negotiation. Accepted []string
// queryCache caches the query result from c.Request.URL.Query(). queryCache url.Values
// formCache caches c.Request.PostForm, which contains the parsed form data from POST, PATCH, // or PUT body parameters. formCache url.Values
// SameSite allows a server to define a cookie attribute making it impossible for // the browser to send this cookie along with cross-site requests. sameSite http.SameSite}
复制代码


相信看到这里,你一定若有所悟了吧。gin 框架用来处理请求响应都是靠 gin.Context 而请求和响应都包含在 gin.Context 当中。


gin 框架的请求方法是通过方法名直接设置的,例如上面例子中的 r.GET 就表示这个接口注册了 GET 方法的调用。下面是 gin 支持的调用方法:


  1. GET:用于从服务器获取资源。

  2. POST:用于向服务器提交数据,通常用于创建资源。

  3. PUT:用于更新服务器上的资源。

  4. DELETE:用于删除服务器上的资源。

  5. PATCH:用于部分更新服务器上的资源。

  6. HEAD:类似于 GET 请求,但只返回响应头,用于获取资源的元数据。

  7. OPTIONS:用于获取服务器支持的 HTTP 方法。

  8. TRACE:用于回显服务器收到的请求,主要用于诊断。


这些方法覆盖了基本的 HTTP 操作,允许客户端与服务器进行各种类型的交互。


下面我们看看处理响应的几种方法:

JSON 响应

router.GET("/json", func(c *gin.Context) {    c.JSON(http.StatusOK, gin.H{        "message": "hello",        "status":  "success",    })})
复制代码

String 响应

router.GET("/string", func(c *gin.Context) {    c.String(http.StatusOK, "Hello, world")})
复制代码

HTML 响应

router.LoadHTMLGlob("templates/*")router.GET("/index", func(c *gin.Context) {    c.HTML(http.StatusOK, "index.tmpl", gin.H{        "title": "Main website",    })})
复制代码


当然还有其他格式的响应,这里就不演示了。

参数解析

路径参数

URL 路径当中参数是最常见的一种类型,在 gin 框架当中,针对这种情况设置了两种类型。第一种是普通的路径参数,另一种是正则匹配的 URL 地址。一开始我也有点懵,下面来看代码演示。


router.GET("/user/:id/*path", func(c *gin.Context) {    id := c.Param("id")    path := c.Param("path")    // 根据id获取用户信息或处理其他逻辑    c.String(http.StatusOK, "User ID: %s Path: %s", id, path)})
复制代码


这里 id 就是普通的路径参数, path 就是匹配参数。假如请求路径是 /user/1/age 匹配结果就是 id=1path=/age。假如路径是 /user/2/account/balance ,那么 id=2 ,而 path=/account/balance 这下就清楚这两者区别,通常不咋会用到匹配的参数。

query 参数

获取 Get 请求参数的常用函数:


  • func (c *Context) Query(key string) string

  • func (c *Context) DefaultQuery(key, defaultValue string) string

  • func (c *Context) GetQuery(key string) (string, bool)


演示代码:


func Handler(c *gin.Context) {  //获取name参数, 通过Query获取的参数值是String类型。  name := c.Query("name")    //获取name参数, 跟Query函数的区别是,可以通过第二个参数设置默认值。    name := c.DefaultQuery("name", "tizi365")  //获取id参数, 通过GetQuery获取的参数值也是String类型,   // 区别是GetQuery返回两个参数,第一个是参数值,第二个参数是参数是否存在的bool值,可以用来判断参数是否存在。  id, ok := c.GetQuery("id")        if !ok {     // 参数不存在  }}
复制代码

POST 参数

获取 Post 请求参数的常用函数:


  • func (c *Context) PostForm(key string) string

  • func (c *Context) DefaultPostForm(key, defaultValue string) string

  • func (c *Context) GetPostForm(key string) (string, bool)


演示代码如下:


func Handler(c *gin.Context) {  //获取name参数, 通过PostForm获取的参数值是String类型。  name := c.PostForm("name")
// 跟PostForm的区别是可以通过第二个参数设置参数默认值 name := c.DefaultPostForm("name", "tizi365")
//获取id参数, 通过GetPostForm获取的参数值也是String类型, // 区别是GetPostForm返回两个参数,第一个是参数值,第二个参数是参数是否存在的bool值,可以用来判断参数是否存在。 id, ok := c.GetPostForm("id") if !ok { // 参数不存在 }}
复制代码

对象参数

前面获取参数的方式都是一个个参数的读取,比较麻烦,Gin框架支持将请求参数自动绑定到一个struct对象,这种方式支持 Get/Post 请求,也支持HTTP请求body内容为json/xml格式的参数。


type User struct {      Name string `json:"name" form:"name"`      Age  int    `json:"age"  form:"age"`  }
复制代码


演示代码如下:


user.GET("/login", func(c *gin.Context) {      u := &User{}      c.ShouldBind(u)      c.JSON(http.StatusOK, u)  })
复制代码

分组路由

在 Gin 框架中,分组路由(Route Groups)是用于组织路由和中间件的一种有效方式。分组路由可以帮助你将相关的路由和中间件组织在一起,使代码更加清晰和易于维护。以下是如何在 Gin 框架中使用分组路由的示例:


user := r.Group("/user")  {      user.GET("/login", func(c *gin.Context) {         u := &User{}         c.ShouldBind(u)         c.JSON(http.StatusOK, u)      })  }
复制代码


非常简单的语法,只需要注册一个 Group 即可,然后组内注册的 HandlerFunc 的路由前缀会加上组的 relativePath 也就是 /user

中间件

在 Gin 框架中,中间件(Middleware)是一个函数,它可以在处理请求之前或之后执行特定的操作。中间件通常用于执行一些通用的任务,比如日志记录、身份验证、跨域资源共享(CORS)处理等。Gin 框架支持全局中间件、路由组中间件和单个路由中间件。


基于 gin.Context 强大的功能,以及 gin 框架的优秀设计,中间件的实现方法依旧是返回 HandlerFunc 即可。下面展示一个打印日志的中间件。


func Logger() gin.HandlerFunc {    return func(c *gin.Context) {        // 开始时间        t := time.Now()        // 处理请求        c.Next()        // 计算执行时间        latency := time.Since(t)        log.Printf("Latency: %v", latency)        // 获取响应状态码        status := c.Writer.Status()        log.Printf("Status: %d", status)    }}
复制代码


中间件的应用的话,有 3 种类型:全局、一组路由、单个路由。下面一次展示使用方法:


全局:


func main() {    router := gin.Default()    // 应用全局中间件    router.Use(Logger())    router.GET("/ping", func(c *gin.Context) {        c.String(http.StatusOK, "pong")    })    router.Run(":8080")}
复制代码


针对一组路由:


func main() {    router := gin.Default()    // 定义一个路由组    apiGroup := router.Group("/api")    {        // 应用中间件到路由组        apiGroup.Use(Logger())        apiGroup.GET("/users", func(c *gin.Context) {            c.String(http.StatusOK, "List of users")        })        apiGroup.GET("/user/:id", func(c *gin.Context) {            id := c.Param("id")            c.String(http.StatusOK, "User ID: %s", id)        })    }    router.Run(":8080")}
复制代码


针对单个路由:


func main() {    router := gin.Default()    // 应用中间件到单个路由    router.GET("/admin", Logger(), func(c *gin.Context) {        c.String(http.StatusOK, "Admin page")    })    router.GET("/ping", func(c *gin.Context) {        c.String(http.StatusOK, "pong")    })    router.Run(":8080")}
复制代码


下面是我打印响应结果的中间件实践:


  func Check() gin.HandlerFunc {      return func(c *gin.Context) {         c.Set("example", "12345")         //path := c.Request.URL.Path         w := &responseWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}         c.Writer = w         c.Next()         responseBody := w.body.String()         logger.Info("response", zap.String("response", responseBody))      }  }
type responseWriter struct { gin.ResponseWriter body *bytes.Buffer } func (w responseWriter) Write(b []byte) (int, error) { w.body.Write(b) return w.ResponseWriter.Write(b) }
复制代码

其他

还有一些其他的常用 API,下面我捡着自己学到的展示一下。


获取客户端 IP:c.ClientIP()


设置模式:


// 设置 release模式  //gin.SetMode(gin.ReleaseMode)  // 或者 设置debug模式  gin.SetMode(gin.DebugMode)
复制代码


Gin 框架提供了两种运行模式:Release 模式Debug 模式。这两种模式主要区别在于日志输出和错误处理方面。通过设置不同的模式,开发者可以更好地适应开发和生产环境的需求。这个看需求选用吧。

发布于: 刚刚阅读数: 4
用户头像

FunTester

关注

公众号:FunTester,800篇原创,欢迎关注 2020-10-20 加入

Fun·BUG挖掘机·性能征服者·头顶锅盖·Tester

评论

发布
暂无评论
gin框架上手实践_FunTester_InfoQ写作社区