go后端框架gin | 青训营笔记

280 阅读5分钟

image.png

这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天

Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确,具有快速灵活,容错方便等特点 gin框架是web框架 参考了这位大佬的文章——[gin框架](Gin框架介绍及使用 | 李文周的博客 (liwenzhou.com))

1、gin简介

插播MVC模型

MVC模型如下所示:
    模型(Model):
      数据库管理与设计。
    控制器(Controller):
      处理用户输入的信息,负责从视图读取数据,控制用户输入,并向模型发送数据源,是应用程序中处理用户交互的部分,负责管理与用户交互控制。
    视图(View):
      将信息显示给用户。

  Gin框架的运行流程如下图所示。

image.png

安装

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

helloworld代码范例

package main

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

func main() {
	// 创建一个默认的路由引擎
	r := gin.Default()
	// GET:请求方式;/hello:请求的路径
	// 当客户端以GET方法请求/hello路径时,会执行后面的匿名函数
	r.GET("/hello", func(c *gin.Context) {
		// c.JSON:返回JSON格式的数据
		c.JSON(200, gin.H{
			"message": "Hello world!",
		})
	})
	// 启动HTTP服务,默认在0.0.0.0:8080启动服务
	r.Run()
}

image.png

web框架所以少不了restful REST的含义就是客户端与Web服务器之间进行交互的时候,使用HTTP协议中的4个请求方法代表不同的动作。

  • GET用来获取资源
  • POST用来新建资源
  • PUT用来更新资源
  • DELETE用来删除资源。

Gin框架支持开发RESTful API的开发。如:

  • 1.获取文章 /blog/getXxx Get blog/Xxx

  • 2.添加 /blog/addXxx POST blog/Xxx

  • 3.修改 /blog/updateXxx PUT blog/Xxx

  • 4.删除 /blog/delXxxx DELETE blog/Xxx

func main() {
	r := gin.Default()
	r.GET("/book", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "GET",
		})
	})

	r.POST("/book", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "POST",
		})
	})

	r.PUT("/book", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "PUT",
		})
	})

	r.DELETE("/book", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "DELETE",
		})
	})
}

这里面的context  Context作为一个数据结构在中间件中传递本次请求的各种数据、管理流程,进行响应在请求来到服务器后,Context对象会生成用来串流程。

Context优点

我们可以 将Request的处理封装到Context中优点

func(context *gin.Context) {
    context.String(http.StatusOK, "some post")
}

参数是gin.Context, 但是查看源码发现其实gin.Context在整个框架处理的地方只有下面这段

 
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    c := engine.pool.Get().(*Context)
    c.writermem.reset(w)
    c.Request = req
    c.reset()
    engine.handleHTTPRequest(c)
    engine.pool.Put(c)
}
 

gin的context实现了的context.Context Interface.经过查看context.Context相关资料, Context的最佳运用场景就是对Http的处理.。封装成Conetxt另外的好处就是WithCancelWithDeadlineWithTimeoutWithValue这些context包衍生的子Context就可以直接来使用. 简单来说就是封装起来可以调用一些API

gin渲染

gin框架可以对html 、静态文件、yml、JSON等。下面给出JSON的范例

func main() {
   r := gin.Default()

   // gin.H 是map[string]interface{}的缩写
   r.GET("/someJSON", func(c *gin.Context) {
   	// 方式一:自己拼接JSON
   	c.JSON(http.StatusOK, gin.H{"message": "Hello world!"})
   })
   r.GET("/moreJSON", func(c *gin.Context) {
   	// 方法二:使用结构体
   	var msg struct {
   		Name    string `json:"user"`
   		Message string
   		Age     int
   	}
   	msg.Name = "小王子"
   	msg.Message = "Hello world!"
   	msg.Age = 18
   	c.JSON(http.StatusOK, msg)
   })
   r.Run(":8080")
}

gin绑定参数和获取参数

querystring指的是URL中?后面携带的参数,例如:/user/search?username=小王子&address=沙河。 获取请求的querystring参数的方法如下:

func main() {
	//Default返回一个默认的路由引擎
	r := gin.Default()
	r.GET("/user/search", func(c *gin.Context) {
		username := c.DefaultQuery("username", "小王子")
		//username := c.Query("username")
		address := c.Query("address")
		//输出json结果给调用方
		c.JSON(http.StatusOK, gin.H{
			"message":  "ok",
			"username": username,
			"address":  address,
		})
	})
	r.Run()
}

我们可以基于请求的Content-Type识别请求数据类型并利用反射机制自动提取请求中QueryStringform表单JSONXML等参数到结构体中。 下面的示例代码演示了.ShouldBind()强大的功能,它能够基于请求自动提取JSONform表单QueryString类型的数据,并把值绑定到指定的结构体对象。

// Binding from JSON
type Login struct {
  User     string `form:"user" json:"user" binding:"required"`
  Password string `form:"password" json:"password" binding:"required"`
}

func main() {
  router := gin.Default()

  // 绑定JSON的示例 ({"user": "q1mi", "password": "123456"})
  router.POST("/loginJSON", func(c *gin.Context) {
  	var login Login

  	if err := c.ShouldBind(&login); err == nil {
  		fmt.Printf("login info:%#v\n", login)
  		c.JSON(http.StatusOK, gin.H{
  			"user":     login.User,
  			"password": login.Password,
  		})
  	} else {
  		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
  	}
  })

  // 绑定form表单示例 (user=q1mi&password=123456)
  router.POST("/loginForm", func(c *gin.Context) {
  	var login Login
  	// ShouldBind()会根据请求的Content-Type自行选择绑定器
  	if err := c.ShouldBind(&login); err == nil {
  		c.JSON(http.StatusOK, gin.H{
  			"user":     login.User,
  			"password": login.Password,
  		})
  	} else {
  		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
  	}
  })

  // 绑定QueryString示例 (/loginQuery?user=q1mi&password=123456)
  router.GET("/loginForm", func(c *gin.Context) {
  	var login Login
  	// ShouldBind()会根据请求的Content-Type自行选择绑定器
  	if err := c.ShouldBind(&login); err == nil {
  		c.JSON(http.StatusOK, gin.H{
  			"user":     login.User,
  			"password": login.Password,
  		})
  	} else {
  		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
  	}
  })

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

gin还有中间件 路由等多种用途

例如中间件对HTTP Request请求进行拦截处理

// 自定义Go中间件 拦截器
func MyMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        // Do something before request
        // 通过自定义的中间件,设置的值,在后续处理只要调用了这个中间件,就可以获取到这个值
        c.Set("usersession", "orzr3-123456")
        c.Next() // 一定要调用该方法,否则后续的处理不会被执行
        // c.Abort() // 一旦调用了该方法,后续的处理都不会被执行
        // Do something after request
    }
}

接收前端传递过来的参数,并且使用中间件

// 接收前端传递过来的参数,并且使用中间件
ginServer.GET("/user/info/handler", MyMiddleware(), func(context *gin.Context) {
    // 获取中间件中设置的值
    userSession := context.MustGet("usersession").(string)
    // 打印
    log.Println("userSession:================>", userSession)
    userid := context.Query("userid")
    username := context.Query("username")
    context.JSON(http.StatusOK, gin.H{
        "userid": userid,
        "username": username,
        "userSession": userSession,
    })
})

不过注意的是 gin.Default()默认使用了LoggerRecovery中间件,其中:

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

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