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