Gin框架
一、准备
1.下载依赖
go get -u github.com/gin-gonic/gin
2.引入依赖
import "github.com/gin-gonic/gin"
3. (可选)如果使用诸如 http.StatusOK 之类的常量,则需要引入 net/http 包
import "net/http"
二、基本使用
1. 配置路由
package main
import "github.com/gin-gonic/gin"
func main() {
// 创建一个默认的路由引擎
r := gin.Default()
// 配置路由
r.GET("/getInfo", func(c *gin.Context) {
c.JSON(200, gin.H{
"msg": "成功",
})
})
//启动 HTTP 服务,在8088端口监听
r.Run(":8088")
}
三、Gin中的路由
1.路由概述
在 RESTful 架构中,每个网址代表一种资源,不同的请求方式表示执行不同的操作:
请求方式 | 一般对应操作 |
---|---|
GET(SELECT) | 从服务器取出资源(一项或多项) |
POST(CREATE)) | 在服务器新建一个资源 |
PUT(UPDATE) | 在服务器更新资源(客户端提供改变后的完整资源) |
DELETE(DELETE)) | 从服务器删除资源 |
2.简单路由
GET请求
r.GET("网址", func(c *gin.Context) {
c.String(200, "Get")
})
POST请求
r.POST("网址", func(c *gin.Context) {
c.String(200, "POST")
})
PUT请求
r.PUT("网址", func(c *gin.Context) {
c.String(200, "PUT")
})
DELETE请求
r.DELETE("网址", func(c *gin.Context) {
c.String(200, "DELETE")
})
动态路由
r.GET("/getUser/:id", func(c *gin.Context) {
id := c.Param("id")
c.JSON(200, gin.H{
"id": id,
"msg": "成功",
})
})
四、 响应格式
这里主要介绍使用比较多的字符串格式和json格式
c.String()
r.GET("/getStr", func(c *gin.Context) {
c.String(200, "成功")
})
响应结果为字符串
c.JSON()
r.GET("/getInfo", func(c *gin.Context) {
c.JSON(200, gin.H{
"msg": "成功",
})
})
响应结果为json格式
五、路由传参
1.GET请求传值
请求路径: /user?uid=20&page=1
后端接收
router.GET("/user", func(c *gin.Context) {
//c.Query()接收请求GET请求路径参数
uid := c.Query("uid")
//c.DefaultQuery()接收请求GET请求路径参数,接收不到时,采用默认参数
page := c.DefaultQuery("page", "0")
c.String(200, "uid=%v page=%v", uid, page)
})
2.Post 请求传值(获取 form 表单)
请求路径:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="/doAddUser" method="post">
用户名:<input type="text" name="username" />
密码: <input type="password" name="password" />
<input type="submit" value="提交">
</form>
</body>
</html>
后端接收
router.POST("/doAddUser", func(c *gin.Context) {
//c.PostForm()接收form表单数据,key要与form表单name属性对应
username := c.PostForm("username")
password := c.PostForm("password")
//c.PostForm()接收form表单数据,接收不到采用默认值,key要与form表单name属性对应
age := c.DefaultPostForm("age", "20")
c.JSON(200, gin.H{ "usernmae": username, "password": password, "age": age, })
})
3.动态路由传值
请求路径:/user/20
后端接收
r.GET("/user/:uid", func(c *gin.Context) {
//c.Param()接收动态路由参数
uid := c.Param("uid")
c.String(200, "userID=%s", uid)
})
4.GET,POST 传递的结构体数据
Get 传值绑定到结构体
请求路径: /?username=zhangsan&password=123456
后端接收
//注意首字母大写
type Userinfo struct {
Username string `form:"username" json:"user"`
Password string `form:"password" json:"password"` }
router.GET("/", func(c *gin.Context) {
var userinfo Userinfo
if err := c.ShouldBind(&userinfo); err == nil {
c.JSON(http.StatusOK, userinfo)
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
})
Post 传值绑定到结构体
请求路径:
后端接收
router.POST("/doLogin", func(c *gin.Context) {
var userinfo Userinfo
if err := c.ShouldBind(&userinfo); err == nil {
c.JSON(http.StatusOK, userinfo)
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
})
.ShouldBind()能够基于请求自动提
取 JSON、form 表单和 QueryString 类型的数据,并把值绑定到指定的结构体对象
六、路由组
func main() {
router := gin.Default()
// 简单的路由组: v1
v1 := router.Group("/v1")
{
v1.POST("/login", loginEndpoint)
v1.POST("/submit", submitEndpoint)
v1.POST("/read", readEndpoint)
}
// 简单的路由组: v2
v2 := router.Group("/v2")
{
v2.POST("/login", loginEndpoint)
v2.POST("/submit", submitEndpoint)
v2.POST("/read", readEndpoint)
}
router.Run(":8080")
}
七、Gin路由文件分组
新建 routes 文件夹,routes 文件下面新建 adminRoutes.goapiRoutes.go、defaultRoutes.go
新建adminRoutes
package routes
import (
"net/http"
"github.com/gin-gonic/gin"
)
func AdminRoutesInit(router *gin.Engine) {
adminRouter := router.Group("/admin")
{
adminRouter.GET("/user", func(c *gin.Context) {
c.String(http.StatusOK, "用户")
})
adminRouter.GET("/news", func(c *gin.Context) {
c.String(http.StatusOK, "news")
})
}
}
新建apiRoutes.go
package routes
import (
"net/http"
"github.com/gin-gonic/gin"
)
func ApiRoutesInit(router *gin.Engine) {
apiRoute := router.Group("/api")
{
apiRoute.GET("/user", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"username": "张三",
"age": 20})
})
apiRoute.GET("/news", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"title": "这是新闻"})
})
}
}
新建defaultRoutes.go
package routes
import (
"github.com/gin-gonic/gin"
)
func DefaultRoutesInit(router *gin.Engine) {
defaultRoute := router.Group("/")
{
defaultRoute.GET("/", func(c *gin.Context) {
c.String(200, "首页")
})
}
}
配置main.go
package main
import (
"gin_demo/routes"
"github.com/gin-gonic/gin"
//注意首字母大写
type Userinfo struct {
Username string `form:"username" json:"user"`
Password string `form:"password" json:"password"`
}
func main() {
r := gin.Default()
routes.AdminRoutesInit(r)
routes.ApiRoutesInit(r)
routes.DefaultRoutesInit(r)
r.Run(":8080")
}
八、Gin中自定义控制器
控制器分组
1.新建controller/admin/NewsController.go
package admin
import (
"net/http"
"github.com/gin-gonic/gin"
)
type NewsController struct {
}
func (c NewsController) Index(ctx *gin.Context) {
ctx.String(http.StatusOK, "新闻首页")
}
2.新建 controller/admin/UserController.go
package admin
import (
"net/http"
"github.com/gin-gonic/gin"
)
type UserController struct {
}
func (c UserController) Index(ctx *gin.Context) {
ctx.String(http.StatusOK, "这是用户首页")
}
func (c UserController) Add(ctx *gin.Context) {
ctx.String(http.StatusOK, "增加用户")
}
3.在路由文件分组中配置控制器(将处理中间件改为控制器的方法)
package routes
import (
"gin_demo/controller/admin"
"github.com/gin-gonic/gin"
)
func AdminRoutesInit(router *gin.Engine) {
adminRouter := router.Group("/admin")
{
adminRouter.GET("/user", admin.UserController{}.Index)
adminRouter.GET("/user/add", admin.UserController{}.Add)
adminRouter.GET("/news", admin.NewsController{}.Add)
}
}
九、路由中间件
通俗的讲:中间件就是匹配路由前和匹配路由完成后执行的一系列操作
Gin 中的中间件必须是一个 gin.HandlerFunc 类型,配置路由的时候可以传递多个 func 回调函
数,最后一个 func 回调函数前面触发的方法都可以称为中间件。
1.路由中间件基本使用
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func initMiddleware(ctx *gin.Context) {
fmt.Println("我是一个中间件")
}
func main() {
r := gin.Default()
r.GET("/", initMiddleware, func(ctx *gin.Context) {
ctx.String(200, "首页--中间件演示")
})
r.GET("/news", initMiddleware, func(ctx *gin.Context) {
ctx.String(200, "新闻页面--中间件演示")
})
r.Run(":8080")
}
2.ctx.Next()调用该请求的剩余处理程序
中间件里面加上 ctx.Next()可以让我们在路由匹配完成后执行一些操作
比如我们统计一个请求的执行时间。
package main
import (
"fmt"
"time"
"github.com/gin-gonic/gin"
)
func initMiddleware(ctx *gin.Context) {
fmt.Println("1-执行中中间件")
start := time.Now().UnixNano()
// 调用该请求的剩余处理程序
ctx.Next()
fmt.Println("3-程序执行完成 计算时间")
// 计算耗时 Go 语言中的 Since()函数保留时间值,并用于评估与实际时间的差异
end := time.Now().UnixNano()
fmt.Println(end - start)
}
func main() {
r := gin.Default()
r.GET("/", initMiddleware, func(ctx *gin.Context) {
fmt.Println("2-执行首页返回数据")
ctx.String(200, "首页--中间件演示")
})
r.GET("/news", initMiddleware, func(ctx *gin.Context) {
ctx.String(200, "新闻页面--中间件演示")
})
r.Run(":8080")
}
3.全局中间件
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func initMiddleware(ctx *gin.Context) {
fmt.Println("全局中间件 通过 r.Use 配置")
// 调用该请求的剩余处理程序
ctx.Next()
}
func main() {
r := gin.Default()
//注册全局中间件
r.Use(initMiddleware)
r.GET("/", func(ctx *gin.Context) {
ctx.String(200, "首页--中间件演示")
})
r.GET("/news", func(ctx *gin.Context) {
ctx.String(200, "新闻页面--中间件演示")
})
r.Run(":8080")
}
4.在路由分组中配置中间件
写法一:
shopGroup := r.Group("/shop", StatCost())
{
shopGroup.GET("/index", func(c *gin.Context) {...})
... }
写法二:
shopGroup := r.Group("/shop")
shopGroup.Use(StatCost())
{
shopGroup.GET("/index", func(c *gin.Context) {...})
... }
十、中间件和对应控制器之间共享数据
1.设置值
func InitAdminMiddleware(ctx *gin.Context) {
// 可以通过 ctx.Set 在请求上下文中设置值,后续的处理函数能够取到该值
ctx.Set("username", "张三")
}
2.获取值
func (c UserController) Index(ctx *gin.Context) {
username, _ := ctx.Get("username")
}
十一、中间件注意事项
gin 默认中间件
gin.Default()默认使用了 Logger 和 Recovery 中间件,其中:
• Logger 中间件将日志写入 gin.DefaultWriter,即使配置了 GIN_MODE=release。
• Recovery 中间件会 recover 任何 panic。如果有 panic 的话,会写入 500 响应码。
如果不想使用上面两个默认的中间件,可以使用 **gin.New()**新建一个没有任何默认中间件的路由
gin 中间件中使用 goroutine
当在中间件或 handler 中启动新的 goroutine 时,不能使用原始的上下文(c *gin.Context),
必须使用其只读副本(c.Copy())
r.GET("/", func(c *gin.Context) {
//拷贝副本
cCp := c.Copy()
go func() {
// simulate a long task with time.Sleep(). 5 seconds
time.Sleep(5 * time.Second)
// 这里使用你创建的副本
fmt.Println("Done! in path " + cCp.Request.URL.Path)
}()
c.String(200, "首页")
})
十二、Gin中的Cookie
设置cookie
c.SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool)
获取cookie
cookie, err := c.Cookie("name")
完整demo
package main
import (
"gin_demo/models"
"github.com/gin-gonic/gin"
"html/template"
)
func main() {
r := gin.Default()
r.SetFuncMap(template.FuncMap{"unixToDate": models.UnixToDate})
r.GET("/", func(c *gin.Context) {
//设置cookie
c.SetCookie("usrename", "张三", 3600, "/", "localhost", false, true)
c.String(200, "首页")
})
r.GET("/user", func(c *gin.Context) {
//获取cookie
username, _ := c.Cookie("usrename")
c.String(200, "用户-"+username)
})
r.Run(":8080")
}
十三、Gin中的Session
Gin 官方没有给我们提供 Session 相关的文档,这个时候我们可以使用第三方的 Session 中间件来实现
session
gin-contrib/sessions 中间件支持的存储引擎:
• cookie
• memstore
• redis
• memcached
• mongodb
1.安装session包
go get github.com/gin-contrib/sessions
2.基本session用法
package main
import (
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// 1.创建基于 cookie 的存储引擎,secret11111 参数是用于加密的秘钥
store := cookie.NewStore([]byte("secret11111"))
// 设置 session 中间件,参数 mysession,指的是 session 的名字,也是 cookie 的名字
// 2.store 是前面创建的存储引擎,我们可以替换成其他存储引擎
r.Use(sessions.Sessions("mysession", store))
r.GET("/", func(c *gin.Context) {
//初始化 session 对象
session := sessions.Default(c)
//设置过期时间
session.Options(sessions.Options{
MaxAge: 3600 * 6, // 6hrs
})
//设置 Session
session.Set("username", "张三")
session.Save()
c.JSON(200, gin.H{"msg": session.Get("username")})
})
r.GET("/user", func(c *gin.Context) {
// 初始化 session 对象
session := sessions.Default(c)
// 通过 session.Get 读取 session 值
username := session.Get("username")
c.JSON(200, gin.H{"username": username})
})
r.Run(":8000")
}