GIN框架详解

介绍

官网:https://gin-gonic.com/zh-cn/

文档:https://gin-gonic.com/zh-cn/docs/

Gin 是一个用 Go (Golang) 编写的 Web 框架。 它具有类似 martini 的 API,性能要好得多,多亏了 httprouter,速度提高了 40 倍。 如果您需要性能和良好的生产力,您一定会喜欢 Gin。

在本节中,我们将介绍 Gin 是什么,它解决了哪些问题,以及它如何帮助你的项目。

或者, 如果你已经准备在项目中使用 Gin,请访问快速入门.

特性

快速

基于 Radix 树的路由,小内存占用。没有反射。可预测的 API 性能。

支持中间件

传入的 HTTP 请求可以由一系列中间件和最终操作来处理。 例如:Logger,Authorization,GZIP,最终操作 DB。

Crash 处理

Gin 可以 catch 一个发生在 HTTP 请求中的 panic 并 recover 它。这样,你的服务器将始终可用。例如,你可以向 Sentry 报告这个 panic!

JSON 验证

Gin 可以解析并验证请求的 JSON,例如检查所需值的存在。

路由组

更好地组织路由。是否需要授权,不同的 API 版本…… 此外,这些组可以无限制地嵌套而不会降低性能。

错误管理

Gin 提供了一种方便的方法来收集 HTTP 请求期间发生的所有错误。最终,中间件可以将它们写入日志文件,数据库并通过网络发送。

内置渲染

Gin 为 JSON,XML 和 HTML 渲染提供了易于使用的 API。

可扩展性

新建一个中间件非常简单,去查看示例代码吧。

安装GIN框架

修改系统环境变量:

go env -w GOPROXY=https://goproxy.cn,direct

修改IDE变量:

GOPROXY=https://goproxy.cn,direct

去项目目录下载GIN:

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

查看go.mod文件

module GIN

go 1.18

require (
	github.com/gin-contrib/sse v0.1.0 // indirect
	github.com/gin-gonic/gin v1.8.1 // indirect
	github.com/go-playground/locales v0.14.0 // indirect
	github.com/go-playground/universal-translator v0.18.0 // indirect
	github.com/go-playground/validator/v10 v10.11.1 // indirect
	github.com/goccy/go-json v0.10.0 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/leodido/go-urn v1.2.1 // indirect
	github.com/mattn/go-isatty v0.0.16 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/pelletier/go-toml/v2 v2.0.6 // indirect
	github.com/ugorji/go/codec v1.2.7 // indirect
	golang.org/x/crypto v0.4.0 // indirect
	golang.org/x/net v0.4.0 // indirect
	golang.org/x/sys v0.3.0 // indirect
	golang.org/x/text v0.5.0 // indirect
	google.golang.org/protobuf v1.28.1 // indirect
	gopkg.in/yaml.v2 v2.4.0 // indirect
	// 安装成功
)

使用GIN

引入GIN包,创建一个简单的响应

package main

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

func main() {
	engine := gin.Default()                    // 创建gin
	engine.GET("/ping", func(c *gin.Context) { // 编写路由
		c.JSON(200, gin.H{ // 返回JSON格式数据 code: 状态码 gin.H 响应体
			"ping": "OK",
		})
	})

	engine.GET("/hello", func(context *gin.Context) {
		context.JSON(200,gin.H{"text":"Hello"})

	})
	//engine.Run()   // 运行
	engine.Run(":8081")  // 指定端口运行
}

测试

C:\Users\Mek>curl mek.030399.xyz/ping
{"ping":"OK"}
C:\Users\Mek>curl 127.0.0.1:8081/hello
{"text":"Hello"}

Context *重要

GIN框架里一个特殊的上下文结构体,用于处理请求和响应

RESTful_API编写

使用GET,POST,PUT,DELETE 四种请求方式处理获取,创建,修改,删除等动作

示例:
package main

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

func main() {
	engine := gin.Default() // 创建gin

	// 编写路由

	engine.GET("/user", func(c *gin.Context) {
		c.JSON(200, gin.H{"text": "user_get"})
	})
	engine.POST("/user", func(c *gin.Context) {
		c.JSON(200, gin.H{"text": "user_post"})
	})
	engine.PUT("/user", func(c *gin.Context) {
		c.JSON(200, gin.H{"text": "user_PUT"})
	})
	engine.DELETE("/user", func(c *gin.Context) {
		c.JSON(200, gin.H{"text": "user_delete"})
	})

	engine.Run() // 运行

}

测试:

使用python的requests模块进行测试

import requests

get = requests.get(url="http://mek.030399.xyz/user").text

print(get)

post = requests.post(url="http://mek.030399.xyz/user").text

print(post)

pul = requests.put(url="http://mek.030399.xyz/user").text

print(pul)

delete = requests.delete(url="http://mek.030399.xyz/user").text

print(delete)
运行结果:
{"text":"user_get"}
{"text":"user_post"}
{"text":"user_PUT"}
{"text":"user_delete"}

接受前端传参

使用Context处理接受GET请求的数据:
package main

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

func main() {
	engine := gin.Default() // 创建gin

	// 编写路由

	engine.GET("/info", func(c *gin.Context) {
		user := c.Query("user") // 使用Context的Query函数通过key获取数据
		passwd := c.Query("passwd")

		c.JSON(200, gin.H{
			"user":   user,
			"passwd": passwd,
		})
	})
	engine.Run() // 运行

}

使用requests测试:
import requests

url = "http://mek.030399.xyz/info"

get = requests.get(url=url, params={'user': 'mek', 'passwd': '1234516'}).text

print(get)  # {"passwd":"1234516","user":"mek"}
接收URL路径传参:
package main

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

func main() {
	engine := gin.Default() // 创建gin

	// 编写路由

    engine.GET("/info/:user/:passwd", func(c *gin.Context) {  // 使用:定义变量名称
		user := c.Param("user") // 使用Param函数获取数据
		passwd := c.Param("passwd")

		c.JSON(200, gin.H{
			"user":   user,
			"passwd": passwd,
		})
	})
	engine.Run() // 运行

}
测试:
C:\Users\Mek>curl mek.030399.xyz/info/mek/passwd
{"passwd":"passwd","user":"mek"}
C:\Users\Mek>
接收json传参:
package main

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

func main() {
	engine := gin.Default() // 创建gin

	// 编写路由

	engine.GET("/info/:user/:passwd", func(c *gin.Context) {
		user := c.Param("user")
		passwd := c.Param("passwd")

		c.JSON(200, gin.H{
			"user":   user,
			"passwd": passwd,
		})
	})

	engine.POST("/json", func(context *gin.Context) {
		data, _ := context.GetRawData()  // 使用GetRawData方法获得请求元数据,是二进制切片格式

		var j map[string]interface{}	// 声明一个map用于接受序列化后的数据

		_ = json.Unmarshal(data, &j)   // 将二进制切片转换为json,并存入变量 j 
		//fmt.Println(string(data))
		//fmt.Println(data)
		//fmt.Println(j)
		context.JSON(200,j)	// 返回

	})

	engine.Run() // 运行

}

使用requests测试:
import requests

url = "http://mek.030399.xyz/json"

dic = {"code": 111, "data": "456789", "info": "999999999"}

print(requests.post(url, json=dic).text)

输出结果:

{"code":111,"data":"456789","info":"999999999"}
接收POST表单传参:
package main

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

func main() {
	engine := gin.Default() // 创建gin

	// 编写路由

	engine.POST("/add", func(context *gin.Context) {
		name := context.PostForm("name") // 使用PostForm 方法接收表单参数
		passwd := context.PostForm("passwd")
		repasswd := context.PostForm("repasswd")

		fmt.Println(name, passwd, repasswd)

		if passwd == repasswd {
			context.JSON(200, gin.H{"name": name, "text": "OK"})

		} else {
			context.JSON(200, gin.H{"name": name, "text": "PASSWDERR"})
		}

	})

	engine.Run() // 运行

}

使用requests测试:
import requests

url = "http://mek.030399.xyz/add"

print(requests.post(url=url, data={'name': 'mek', 'passwd': '1111', 'repasswd': '1111'}).text)
print(requests.post(url=url, data={'name': 'mek', 'passwd': '1111', 'repasswd': '11111'}).text)

# {"name":"mek","text":"OK"}
# {"name":"mek","text":"PASSWDERR"}

重定向

使用Redirect方法实现重定向: *注意状态码 301
package main

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

func main() {
	engine := gin.Default() // 创建gin

	// 编写路由

	engine.GET("/r", func(context *gin.Context) {
		context.Redirect(301,"https://030399.xyz")
	})

	engine.Run() // 运行

}

路由组

使用Group定义路由组:
package main

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

func main() {
	engine := gin.Default() // 创建gin

	// 创建路由组

	userGroup := engine.Group("/user") //  user/xxx
	{
		// 撰写路由组路由

		userGroup.GET("/add", func(context *gin.Context) { // = user/add
			context.JSON(200, gin.H{"path": "user/add"})
		})
		userGroup.GET("/info", func(context *gin.Context) { // = user/info
			context.JSON(200, gin.H{"path": "user/info"})
		})
	}

	adminGroup := engine.Group("/admin")
	{
		adminGroup.GET("/load")
		adminGroup.GET("/exit")
	}

	engine.Run() // 运行

}

测试:
C:\Users\Mek>curl mek.030399.xyz/user/add
{"path":"user/add"}
C:\Users\Mek>curl mek.030399.xyz/user/info
{"path":"user/info"}
C:\Users\Mek>

中间件 - 拦截器

中间件是一个函数,在路由函数处理之前预处理请求

编写中间件:

中间件函数 需要返回一个gin.HandlerFunc结构体

有两个特殊函数

context.Next() 放行到路由函数

context.Abort() 拦截请求

package main

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

func Hander() gin.HandlerFunc {   // 编写中间件
	return func(context *gin.Context) {
		//在这里写需要操作的代码
		//在这里写需要操作的代码
		context.Set("111", "111") // 给请求设置参数

		//获取传参
		user := context.Query("user")
		fmt.Println(user)
		
        // 判断user是不是=admin 是就放行,不是就拦截
        
		if user == "admin" {
			// 放行函数

			context.Next()
		} else {
			// 拦截函数
			context.Abort()
		}

	}
}

func main() {
	engine := gin.Default() // 创建gin

	// 使用中间件
	engine.GET("/load",Hander(), func(context *gin.Context) {
		context.JSON(200, gin.H{"code": "OK"})
	})

	engine.Run() // 运行

}

测试:
C:\Users\Mek>curl mek.030399.xyz/load?user=admin
{"code":"OK"}
C:\Users\Mek>curl mek.030399.xyz/load?user=111

C:\Users\Mek>

设置和获取Cookie

package main

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

func main() {
	engine := gin.Default() // 创建gin

	// 创建路由组

	engine.GET("/load", func(c *gin.Context) {

		cookie, err := c.Cookie("gin_cookie") // 使用Cookie方法获取Cookie,需要传一个key
		if err != nil {
			cookie = "NotSet"

			// 使用SetCookie 方法设置Coockie  依次传入key,value,失效时间,作用路径,作用域
			c.SetCookie("gin_cookie", "test", 3600, "/", "127.0.0.1", false, false)
		}
		c.JSON(200, gin.H{"code": "OK"})
		fmt.Printf("Cookie value: %s \n", cookie)
	})

	engine.Run() // 运行

}

测试:

import requests

url = "http://mek.030399.xyz/load"

r = requests.get(url)
print(r.cookies.values())   # ['test']


将日志输出到文件

func main() {
    // 禁用控制台颜色,将日志写入文件时不需要控制台颜色。
    gin.DisableConsoleColor()

    // 记录到文件。
    f, _ := os.Create("gin.log")
    gin.DefaultWriter = io.MultiWriter(f)

    // 如果需要同时将日志写入文件和控制台,请使用以下代码。
    // gin.DefaultWriter = io.MultiWriter(f, os.Stdout)

    router := gin.Default()
    router.GET("/ping", func(c *gin.Context) {
        c.String(200, "pong")
    })

    router.Run(":8080")
}