Gin框架
Gin框架
一、Gin框架使用
1、Gin框架简介
Gin是一个用Go语言编写的Web框架。Gin框架拥有很好的性能,借助高性能的HttpRouter包,运行速度得到了极大的提升。
2、Gin框架安装与示例
(1)、安装
go get -u github.com/gin-gonic/gin
(2)、示例
func Get(c *gin.Context) {
name := c.Query("name") // 从url中获取name字段
// 返回字符串格式的数据
//c.String(200, "欢迎您:%s", name)
// c.JSON :返回JSON格式的数据
c.JSON(200, gin.H{
"code": http.StatusOK,
"msg": "返回信息",
"data": "欢迎您" + name,
})
}
func main() {
// 创建一个默认的路由引擎
r := gin.Default()
// GET:请求方式:/hello:请求的路径,当客户端以Get方法请求/hello路径时,会执行后面的函数
r.GET("/hello", Get)
//r.POST("/user/login", Post)
// 启动HTTP服务,默认在0.0.0.0:8080启动服务
r.Run("127.0.0.1:8090")
}
3、Gin路由和控制器
路由:一个HTTP请求找到对应 的处理器函数的过程。处理器函数主要负责执行HTTP请求和响应任务。如下代码中的goLogin() 函数就是Gin的处理器函数。
func goLogin(context *gin.Context) {
name := context.PostForm("username")
password := context.PostForm("password")
if name == "dujie" && password == "123456" {
context.JSON(200, gin.H{
"code": http.StatusOK,
"msg": "登录成功",
"username": name,
})
} else {
context.JSON(http.StatusOK, gin.H{
"code": 403,
"msg": "用户或密码错误",
})
}
//context.String(200, "username=%s password=%s", name, password)
}
####### (1)、路由规则
一条路由规则由HTTP请求方法、URL路径、处理器函数这3个部分组成
- HTTP请求方法
常用的HTTP请求方法有GET、POST、PUT、DELETE等
- URL路径
Gin框架的URL路径有3种写法
1)静态URL路径,即不带任何参数的URL路径,例如:
/users/dujie
/user/1
/article/6
2)带路径参数的URL路径,URL路径中带有参数,参数由英文冒号”:” 跟着一个字符串定义。例如:
定义参数:id
3)带星号(*)模糊匹配参数的URL路径
星号(*)代表匹配任意路径的意思。必须在 * 后面指定一个参数名,之后可以通过这个参数获取 * 号匹配的内容。例如:”/user/*path” 可以通过path参数获取 * 号匹配的内容,如/user/1 、/user/dujie/comment/1等。
- 处理器函数
Gin框架的处理器函数定义如下:
type HandlerFunc func(*Context)
处理器函数接受一个上下文参数。可以通过上下文参数获取HTTP的请求参数,返回HTTP请求的响应。
####### (1)、分组路由
在做API开发时,如果要支持多个API版本,则可以通过分组路由来处理API版本。
func main() {
// 创建一个默认的路由引擎
r := gin.Default()
// GET:请求方式:/hello:请求的路径,当客户端以Get方法请求/hello路径时,会执行后面的函数
v1 := r.Group("/v1")
{
v1.POST("/login", goLogin)
}
v2 := r.Group("/v2")
{
v2.POST("/login", goLogin)
}
r.Run(":8090")
}
func goLogin(context *gin.Context) {
fmt.Println("1:", context.Params)
fmt.Println("2:", context.Request)
fmt.Println("3:", context.Keys)
name := context.PostForm("username")
password := context.PostForm("password")
if name == "dujie" && password == "123456" {
context.JSON(200, gin.H{
"code": http.StatusOK,
"msg": "登录成功",
"username": name,
})
} else {
context.JSON(http.StatusOK, gin.H{
"code": 403,
"msg": "用户或密码错误",
})
}
//context.String(200, "username=%s password=%s", name, password)
}
上面的例子会将注册下面的路由信息:
/v1/login
/v2/login
4、Gin处理请求参数
(1)、获取GET请求参数
Gin获取GET请求参数常用方法如下:
// Query returns the keyed url query value if it exists,
// otherwise it returns an empty string `("")`.
// It is shortcut for `c.Request.URL.Query().Get(key)`
//
// GET /path?id=1234&name=Manu&value=
// c.Query("id") == "1234"
// c.Query("name") == "Manu"
// c.Query("value") == ""
// c.Query("wtf") == ""
func (c *Context) Query(key string) (value string) {
value, _ = c.GetQuery(key)
return
}
// DefaultQuery returns the keyed url query value if it exists,
// otherwise it returns the specified defaultValue string.
// See: Query() and GetQuery() for further information.
//
// GET /?name=Manu&lastname=
// c.DefaultQuery("name", "unknown") == "Manu"
// c.DefaultQuery("id", "none") == "none"
// c.DefaultQuery("lastname", "none") == ""
func (c *Context) DefaultQuery(key, defaultValue string) string {
if value, ok := c.GetQuery(key); ok {
return value
}
return defaultValue
}
// DefaultQuery returns the keyed url query value if it exists,
// otherwise it returns the specified defaultValue string.
// See: Query() and GetQuery() for further information.
//
// GET /?name=Manu&lastname=
// c.DefaultQuery("name", "unknown") == "Manu"
// c.DefaultQuery("id", "none") == "none"
// c.DefaultQuery("lastname", "none") == ""
func (c *Context) DefaultQuery(key, defaultValue string) string {
if value, ok := c.GetQuery(key); ok {
return value
}
return defaultValue
}
(2)、获取POST请求参数
Gin获取POST请求参数的常用方法如下:
// PostForm returns the specified key from a POST urlencoded form or multipart form
// when it exists, otherwise it returns an empty string `("")`.
func (c *Context) PostForm(key string) (value string) {
value, _ = c.GetPostForm(key)
return
}
// DefaultPostForm returns the specified key from a POST urlencoded form or multipart form
// when it exists, otherwise it returns the specified defaultValue string.
// See: PostForm() and GetPostForm() for further information.
func (c *Context) DefaultPostForm(key, defaultValue string) string {
if value, ok := c.GetPostForm(key); ok {
return value
}
return defaultValue
}
// GetPostForm is like PostForm(key). It returns the specified key from a POST urlencoded
// form or multipart form when it exists `(value, true)` (even when the value is an empty string),
// otherwise it returns ("", false).
// For example, during a PATCH request to update the user's email:
//
// email=mail@example.com --> ("mail@example.com", true) := GetPostForm("email") // set email to "mail@example.com"
// email= --> ("", true) := GetPostForm("email") // set email to ""
// --> ("", false) := GetPostForm("email") // do nothing with email
func (c *Context) GetPostForm(key string) (string, bool) {
if values, ok := c.GetPostFormArray(key); ok {
return values[0], ok
}
return "", false
}
使用方法示例:
// 跟PostForm的区别是:可以通过第2个参数设置参数默认值
name := context.DefaultPostForm("username", "name")
//name := context.PostForm("username")
password, ok := context.GetPostForm("password")
if !ok {
log.Panicln("参数不存在")
}
(3)、获取URL路径参数
Gin获取URL路径参数是指,获取/user/:id 这类路由绑定的参数。/user/:id 绑定了一个参数id。获取URL路径参数的函数如下:
func (c *Context) Param(key string) string {
return c.Params.ByName(key)
}
r.GET("/user/:id", func(context *gin.Context) {
id := context.Param("id")
fmt.Println(id)
if id == "1" {
context.JSON(200, gin.H{
"code": 200,
"msg": User{
UserName: "dujie",
Age: 32,
Phone: "15811047166",
},
})
} else {
context.JSON(200, gin.H{
"code": "404",
"msg": "无此用户!!!",
})
}
})
(4)、将请求参数绑定到结构体
前面获取参数的方式都是逐个进行参数的读取,比较麻烦。Gin支持将请求参数自动绑定到一个结构体对象,这种方式支持GET/POST请求,也支持HTTP请求体中内容为JSON或XML格式的参数。
type User struct {
UserName string `json:"userName" form:"username"`
Phone string `json:"phone" form:"phone"`
Age int `json:"age" form:"age"`
}
标签 | 说明 |
---|---|
json:”userName” | 数据为JSON格式,并且json字段名为userName |
form:”phone” | 表单参数名为phone |
模拟添加用户:
r.POST("/user/:id", func(context *gin.Context) {
u := User{}
if context.ShouldBind(&u) == nil {
log.Println(u.UserName)
log.Println(u.Phone)
log.Println(u.Age)
}
context.JSON(200, gin.H{
"code": http.StatusOK,
"msg": u,
})
//context.String(200, "Success")
})
context.ShouldBind(&u)
用于将请求中的数据绑定到指定的结构体
5、Gin生成HTTP请求响应
Gin支持以字符串、JSON、XML、文件等格式生成HTTP请求响应。gin.Context 上下文对象支持多种返回处理结果。
(1)以字符串方式生成HTTP请求响应。
通过String()方法生成字符串方式的HTTP请求响应。String()方法的定义如下:
// String writes the given string into the response body.
func (c *Context) String(code int, format string, values ...any) {
c.Render(code, render.String{Format: format, Data: values})
}
该方法使用示例如下:
func Handler(c *gin.Context){
c.String(200,"hello%s ,欢迎%s","go","dujie")
}
(2)以JSON格式生成HTTP请求响应。
实际开发API接口时,最常用的就是JSON
r.POST("/user/:id", func(context *gin.Context) {
u := User{}
if context.ShouldBind(&u) == nil {
log.Println(u.UserName)
log.Println(u.Phone)
log.Println(u.Age)
}
context.JSON(http.StatusOK, gin.H{
"code": http.StatusOK,
"msg": u,
})
})
(3)以XML格式生成HTTP请求响应。
定义一个User结构体,默认结构体的名字就是XML的根节点名字。
type User struct {
UserName string `xml:"userName" form:"username"`
Phone string `xml:"phone" form:"phone"`
Age int `xml:"age" form:"age"`
}
r.GET("/user/:id", func(context *gin.Context) {
id := context.Param("id")
fmt.Println(id)
if id == "1" {
u := &User{
UserName: "dujie",
Age: 32,
Phone: "15811047166",
}
context.XML(200, u)
} else {
}
(4)以文件格式生成HTTP请求响应。
通过File() 方法直接返回本地文件,参数为本地文件地址
func(c *gin.Context){
// 通过File()方法直接返回本地丈件’参数为本地丈件地址 ′
c.File("/var/www/gin/test.jpg")
}
(5)设置HTTP响应头。
Gin中提供了Header()
方法来设置HTTP响应头。默认采用key/value方式,支持设置多个Header
r.GET("/user/:id", func(context *gin.Context) {
id := context.Param("id")
fmt.Println(id)
context.Header("Content-Type", "application/json")
context.Header("site", "dujie")
}
(6)Gin处理静态文件
如果项目中包含JS、CSS、JPG之类的静态文件,下面的例子:
func main() {
// 创建一个默认的路由引擎
r := gin.Default()
r.Static("/assets","/var/www/gin/assets")
r.StaticFile("/favicon.ico","./static/favicon.ico")
r.Run(":8080")
}
(7)Gin处理cookie
Gin主要通过上下文对象提供的SetCookie()
和Cookie()
两个方法操作cookie,这两个函数都是对go语言net/http
包中http.SetCookie()
方法的重新封装,本质是一样
- 设置cookie
Gin使用SetCookie()方法设置cookie。
// SetCookie adds a Set-Cookie header to the ResponseWriter's headers.
// The provided cookie must have a valid Name. Invalid cookies may be
// silently dropped.
func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) {
if path == "" {
path = "/"
}
http.SetCookie(c.Writer, &http.Cookie{
Name: name, // Cookie 的名称
Value: url.QueryEscape(value), // Cookie的值
MaxAge: maxAge, // 过期时间(秒)
Path: path, // 路径
Domain: domain, // 域
SameSite: c.sameSite,
Secure: secure, // 是否只允许HTTPs访问
HttpOnly: httpOnly, // 是否允许浏览器仅通过HTTP获取Cookie
})
}
r.GET("/user/:id", func(context *gin.Context) {
context.SetCookie("my_cookie", "cookievalue", 3600, "/", "localhost", false, true)
id := context.Param("id")
fmt.Println(id)
context.Header("Content-Type", "application/json")
context.Header("site", "dujie")
if id == "1" {
u := User{
UserName: "dujie",
Age: 32,
Phone: "15811047166",
}
//context.XML(200, u)
context.JSON(200, gin.H{
"code": 200,
"msg": u,
})
} else {
context.JSON(200, gin.H{
"code": "404",
"msg": "无此用户!!!",
})
}
})
- 读取Cookie
Gin使用Cookie() 方法读取cookie
func main() {
r := gin.Default()
r.GET("/user/:id", func(context *gin.Context) {
//context.SetCookie("cookie", "value", 3600, "/", "127.0.0.1", false, true)
cookie, err := context.Cookie("cookie")
if err != nil {
log.Panicln(err)
}
- 删除Cookie
将SetCookie() 方法的MaxAge参数设置为-1,就可以删除cookie
context.SetCookie("cookie", "value", -1, "/", "127.0.0.1", false, true)
(8)Gin文件上传
Gin使用SaveUploadFile()
方法实现文件上传
func main() {
r := gin.Default()
r.MaxMultipartMemory = 64 <<20
r.POST("/upload", func(context *gin.Context) {
// file 是表单字段名字
file,_ := context.FormFile("file")
// 打印上传的文件名
log.Println(file.Filename)
// 将上传的文件保存到./dujie.jpg中
context.SaveUploadedFile(file,"./dujie.jpg")
context.String(200,fmt.Sprintf("'%s' uploaded!",file.Filename))
})
r.Run(":8090")
}
(9)Gin中间件
Gin中,中间件(Middleware)是指可以拦截HTTP请求——响应生命周期的特殊函数。在请求——响应生命周期中可以注册多个中间件。每个中间件执行不同的功能,一个中间件执行完,才轮到下一个中间件执行,中间件常用的应用场景如下:
- 请求限速
- API接口签名处理
- 权限校验
- 统一错误处理
如果想拦截所有请求,则可以开发一个中间件函数来实现。Gin支持设置全局中间件和针对路由分组的中间件。在设置全局中间件后,会拦截所有请求。通过分组路由设置的中间件,仅对这个分组下的路由起作用
(9.1)使用中间件
Gin中使用Use()
方法来使用中间件
func main() {
r := gin.Default()
// 通过Use()方法设置中间件
// 设置日志中间件,主要用于打印请求日志
r.Use(gin.Logger())
// 设置Recovery中间件,主要用于拦截panic错误,不至于导致程序崩溃
r.Use(gin.Recovery())
r.GET("/user/:id", func(context *gin.Context) {
context.JSON(200, gin.H{
"code": 200,
"msg": "成功",
})
})
r.Run(":8090")
}
(9.2)自定义中间件
func Logger() gin.HandlerFunc {
return func(context *gin.Context) {
t := time.Now()
// 可以通过上下文对象,设置一些依附在上下文对象里面的键值数据
context.Set("example", "这是一个中间件数据")
// 在这里处理请求到达处理器函数之前的逻辑
// 调用下一个中间件,或者处理器的处理函数,具体得看注册了多少个中间件
context.Next()
// 在这里可以处理返给客户端之前的响应逻辑
latency := time.Since(t)
log.Println(latency)
// 例如,查询请求状态码
status := context.Writer.Status()
log.Println(status)
}
}
func main() {
r := gin.Default()
// 注册上面自定义的日志中间件
r.Use(Logger())
r.GET("/hello", func(context *gin.Context) {
// 获取日志中间件中注入的键值数据,接口断言是否是string类型
example := context.MustGet("example").(string)
log.Println(example)
})
r.Run(":8090")
}
// 运行访问后输出
[GIN-debug] GET /hello --> main.main.func1 (4 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8090
2023/08/30 09:07:53 这是一个中间件数据
2023/08/30 09:07:53 256.417µs
2023/08/30 09:07:53 200
[GIN] 2023/08/30 - 09:07:53 | 200 | 369.125µs | 127.0.0.1 | GET "/hello"
(10)Gin处理session
在Gin中,依赖”github.com/gin-contrib/sessions”包中的中间件处理session。该包中的中间件支持cookie、MemStore、Redis、Memcached、Mongodb引擎。
1、安装
go get -u github.com/gin-contrib/sessions
2、用法示例
func main() {
r := gin.Default()
// 创建基于cookie的存储引擎,password123456参数是用于加密的秘钥
store := cookie.NewStore([]byte("password123456"))
// 设置session中间件,参数my_session 指的是session的名字,也是cookie的名字
// store是前面创建的存储引擎,可以将其替换成其他存储引擎,如Redis等
r.Use(sessions.Sessions("my_session", store))
r.GET("/hello", func(context *gin.Context) {
// 初始化session对象
session := sessions.Default(context)
// 通过session Get()函数读取session值
// session 是键值对格式数据,因此需要通过key查询数据
if session.Get("hello") != "world" {
// 设置session数据
session.Set("hello", "world")
// 删除session数据
session.Delete("dujie")
// 保存session数据
session.Save()
// 删除整个session
// session.Clear()
}
context.JSON(200, gin.H{
"hello": session.Get("hello"),
})
})
// 获取session
r.GET("/session", Handler)
r.Run(":8090")
}
// 获取session的handler函数
func Handler(c *gin.Context) {
data, err := c.Cookie("my_session")
fmt.Println(data)
fmt.Println(err)
if err == nil {
c.String(200, data)
return
}
c.String(200, "not found")
}
3、基于Redis存储引擎的session
如果想将session数据保存到Redis中,则只要将session的存储引擎改成Redis即可
安装Gin的Redis存储引擎包
go get -u github.com/gin-contrib/sessions/redis
基于Redis存储引擎示例
func main() {
r := gin.Default()
//store := cookie.NewStore([]byte("password123456"))
// 初始化基于redis的存储引擎
store, err := redis.NewStore(10, "tcp", "localhost:6379", "", []byte("password"))
if err != nil {
log.Panicln(err)
}
r.Use(sessions.Sessions("my_session", store))
r.GET("/hello", func(context *gin.Context) {
session := sessions.Default(context)
var count int
v := session.Get("count")
if v == nil {
count = 0
} else {
count = v.(int)
count++
}
session.Set("count", count)
session.Save()
context.JSON(200, gin.H{
"count": count,
})
})
r.GET("/session", Handler)
r.Run(":8090")
}
func Handler(c *gin.Context) {
// 上下文中获取session
session := sessions.Default(c)
count := session.Get("count")
fmt.Println("————————", count)
if count != nil {
c.JSON(200, gin.H{
"count": count,
})
return
}
c.String(200, "not found")
}