gin学习
一、Gin 介绍
- Gin 是一个 Go (Golang) 企业管理系统定制开发编写的轻量级 http web 框架,企业管理系统定制开发运行速度非常快,企业管理系统定制开发如果你是性能和高效的追求者
- Gin 企业管理系统定制开发最擅长的就是 Api 企业管理系统定制开发接口的高并发,企业管理系统定制开发如果项目的规模不大,企业管理系统定制开发业务相对简单,企业管理系统定制开发这个时候我们也推荐您使用 Gin
- Gin 的官网:
- Github 地址:
二、Gin 环境搭建
- 要安装 Gin 软件包,企业管理系统定制开发需要先安装 Go 并设置 Go 工作区(demo)
- 企业管理系统定制开发下载并安装 gin
$ go get -u github.com/gin-gonic/gin
- 将 gin 企业管理系统定制开发引入到代码中
import "github.com/gin-gonic/gin"
- (可选)企业管理系统定制开发如果使用诸如 http.StatusOK 企业管理系统定制开发之类的常量,企业管理系统定制开发则需要引入 net/http 包:
import "net/http"
- 进入项目
demo
根目录, 第三方包用mod.mod 管理
$ go mod init demo
- 新建
main.go
配置路由
package mainimport ( "github.com/gin-gonic/gin")func main() { // 创建一个默认的路由引擎 r := gin.Default() // 配置路由 r.GET("/", func(c *gin.Context) { c.JSON(200, gin.H{ "username": "name1", "data": "data1", }) }) // 启动 HTTP 服务,默认在 0.0.0.0:8080 启动服务 r.Run()}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 运行你的项目
$ go run main.go
- 可改变默认启动端口
`r.Run(":9000")
三、golang 程序的热加载
- 所谓热加载就是当我们对代码进行修改时,程序能够自动重新加载并执行,这在我们开发中 是非常便利的,可以快速进行代码测试,省去了每次手动重新编译
- 工具一:
- 安装fresh
在GOPATH下bin目录下有fresh.exe,没有的话,就是安装失败
$cd demo$go get github.com/pilu/fresh$fresh
- 1
- 2
- 3
其中:
当运行fresh出现下列错误的时候的解决办法
fresh : 无法将“fresh”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。所在位置 行:1 字符: 1+ fresh+ ~~~~~ + CategoryInfo : ObjectNotFound: (fresh:String) [], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundException
- 1
- 2
- 3
- 4
- 5
- 6
- 手动下载fresh包
git clone https://github.com/gravityblast/fresh.git
- 安装fresh
go install github.com/pilu/fresh@latest
- 在GOPATH下bin目录下有fresh.exe 如图
- 工具二:
命令:
go get github.com/codegangsta/gin
gin run main.go
四、Gin 框架中的路由
4.1 路由概述
路由(Routing)是由一个 URI(或者叫路径)和一个特定的 HTTP 方法(GET、POST 等) 组成的,涉及到应用如何响应客户端对某个网站节点的访问
- RESTful API 是目前比较成熟的一套互联网应用程序的 API 设计理论,所以我们设计我们的路由的时候建议参考 RESTful API 指南。
- 在 RESTful 架构中,每个网址代表一种资源,不同的请求方式表示执行不同的操作:
请求资源 | 说明 |
---|---|
GET (SELECT) | 从服务器取资源 |
POST(CREATE) | 新建资源 |
PUT (UPDATE) | 更新资源 |
DELETE | 删除资源 |
4.2 简单的路由配置
- 简单的路由配置(可以通过 postman 测试)
- 当用 GET 请求访问一个网址的时候,做什么事情:
r.GET("/", func(c *gin.Context) { c.String(200, "hello world")})
- 1
- 2
- 3
- 路由里面获取 Get 传值,
域名/news?aid=20
r.GET("/news", func(c *gin.Context) { aid := c.Query("aid") c.JSON(200, gin.H{ "news": aid, // aid = 20 }) })
- 1
- 2
- 3
- 4
- 5
- 6
其中:H 为 map[string]interface{}
类型
4.3 动态路由
域名/user/20
r.GET("/user/:id", func(c *gin.Context) { id := c.Param("id") c.JSON(200, gin.H{ "news": id, // id 20 }) })
- 1
- 2
- 3
- 4
- 5
- 6
4.4 c.String() c.JSON() c.JSONP() c.XML() c.HTML()
4.4.1 返回一个字符串
r.GET("/news", func(c *gin.Context) { aid := c.Query("aid") c.String(200, "string") })
- 1
- 2
- 3
- 4
4.4.2 返回一个 JSON 数据
r.GET("/json", func(c *gin.Context) { aid := c.Query("aid") // 方式一:自己拼接 JSON c.JSON(http.StatusOK, gin.H{ "msg": "JSON" }) }) r.GET("/structJson", func(c *gin.Context) { // 结构体方式 var msg struct { Username string `json:"username"` Msg string `json:"msg"` Age string `json:"age"` } msg.Username = "name1" msg.Msg = "msg1" msg.Age = "18" c.JSON(200, msg) })
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
4.4.3 返回JSOPN
r.GET("/JSONP", func(c *gin.Context) { data := map[string]interface{}{ "foo": "bar", } // /JSONP?callback=x // 将输出:x({\"foo\":\"bar\"}) c.JSONP(http.StatusOK, data)})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
4.4.4 渲染模板
r.GET("/", func(c *gin.Context) { c.HTML( http.StatusOK, "default/index.html", map[string]interface{}{ "title": "前台首页" }) })
- 1
- 2
- 3
- 4
- 5
- 6
五、Gin HTML 模板渲染
5.1 全部模板放在一个目录里面的配置方法
- 我们首先在项目根目录新建 templates 文件夹,然后在文件夹中新建 index.html
<!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><h1>这是一个 html 模板</h1><h3>{{.title}}</h3></body></html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- Gin 框架中使用 c.HTML 可以渲染模板, 渲染模板前需要使用 LoadHTMLGlob() 或者LoadHTMLFiles()方法加载模板。
r.GET("/", func(c *gin.Context) { c.HTML(http.StatusOK, "default/index.html", map[string]interface{}{"title": "前台首页"}) }) r.GET("/", func(c *gin.Context) { c.HTML(http.StatusOK, "index.html", gin.H{ "title": "Main website", }) })
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
5.2 模板放在不同目录里面的配置方法
5.3 gin 模板基本语法
六、静态文件服务
- 当我们渲染的 HTML 文件中引用了静态文件时,我们需要配置静态 web 服务
r.Static("/static", “./static”) 前面的/static 表示路由 后面的./static 表示路径
r := gin.Default() r.Static("/static", "./static") r.LoadHTMLGlob("templates/**/*")
- 1
- 2
- 3
七、路由详解
- 路由(Routing)是由一个 URI(或者叫路径)和一个特定的 HTTP 方法(GET、POST 等) 组成的,涉及到应用如何响应客户端对某个网站节点的访问。
- 前面章节我们给大家介绍了路由基础以及路由配置,这里我们详细给大家讲讲路由传值、路 由返回值
7.1 GET POST 以及获取 Get Post 传值
7.1.1 Get 请求传值
GET /user?uid=20&page=1
r.GET("/user", func(c *gin.Context) { uid := c.Query("uid") page := c.DefaultQuery("page", "0") c.String(200, "uid=%v page=%v", uid, page) })
- 1
- 2
- 3
- 4
- 5
7.1.2 动态路由传值
get /user/20
r.GET("/user/:id", func(c *gin.Context) { id := c.Param("id") c.JSON(200, gin.H{ "news": id, // id 20 }) })
- 1
- 2
- 3
- 4
- 5
- 6
7.1.3 Post 请求传值 获取 form 表单数据
- 通过
c.PostForm
接收表单传过来的数据 postman
进行数据提交
r.POST("/doAddUser", func(c *gin.Context) { username := c.PostForm("username") password := c.PostForm("password") age := c.DefaultPostForm("age", "20") c.JSON(200, gin.H{ "usernmae": username, "password": password, "age": age, }) })
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
7.1.4 Get 传值绑定到结构体
/?username=zhangsan&password=123456
type Userinfo struct { Username string `form:"username" json:"user"` Password string `form:"password" json:"password"`}func main() { // 创建一个默认的路由引擎 r := gin.Default() // 配置路由 r.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()}) } })}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 返回数据
{"user":"zhangsan","password":"123456"}
7.1.5 Post 传值绑定到结构体
r.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()}) } })
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
7.1.6 获取 Post Xml 数据
- 通过
ctx.ShouldBindXML
获取xml中的数据
<?xml version="1.0" encoding="UTF-8"?><article> <content type="string">我是张三</content> <title type="string">张三</title></article>
- 1
- 2
- 3
- 4
- 5
type Article struct { Title string `json:"title" xml:"title"` Content string `json:"content" xml:"content"`} r.POST("/xml", func(ctx *gin.Context) { var article Article if err := ctx.ShouldBindXML(&article); err == nil { ctx.JSON(http.StatusOK, article) }else { ctx.JSON(http.StatusBadRequest, gin.H { "err": err.Error()}) } })
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 通过
ctx.GetRawData()
获取数据
7.2 路由分组
r := gin.Default() apiRouter := r.Group("/api") { apiRouter.GET("/", func(ctx *gin.Context) { ctx.String(200, "api") }) apiRouter.GET("articles", func(ctx *gin.Context) { ctx.String(200, "/api/articles") }) }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
7.3 路由分离
- 在项目新建文件夹
router
,然后在router
目录下创建apiRouter.go
和adminRouter.go
,内容如下:
package router// apiRouter.goimport "github.com/gin-gonic/gin"func ApiRouter(r *gin.Engine) { apiRouter := r.Group("/api") { apiRouter.GET("/", func(ctx *gin.Context) { ctx.String(200, "api") }) apiRouter.GET("articles", func(ctx *gin.Context) { ctx.String(200, "/api/articles") }) }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
package router// adminRouter.goimport ( "net/http" "github.com/gin-gonic/gin")func AdminRouter(r *gin.Engine) { adminRouter := r.Group("/admin") { adminRouter.GET("/", func(ctx *gin.Context) { ctx.String(200, "admin") }) adminRouter.GET("list", func(ctx *gin.Context) { ctx.String(http.StatusOK, "admin/list") }) }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 在
main.go
中引入路由模块
package mainimport ( "socket/router" "github.com/gin-gonic/gin")func main() { // 创建一个默认的路由引擎 r := gin.Default() // 引入路由模块 router.AdminRouter(r) router.ApiRouter(r) // 启动 HTTP 服务,默认在 0.0.0.0:8080 启动服务 r.Run()}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
八、Gin 中自定义控制器
8.1 控制器分组
- 当我们的项目比较大的时候有必要对我们的控制器进行分组 , 业务逻辑放在控制器中
- 在项目文件夹下面新建
controller\api\
文件夹,创建userController.go
mkdir controllercd controllermkdir api
- 1
- 2
- 3
userController.go
内容
package apiimport "github.com/gin-gonic/gin"func UserIndex(c *gin.Context) { c.String(200, "api UserIndex")}func UserAdd(c *gin.Context) { c.String(200, "api UserAdd")}func UserList(c *gin.Context) { c.String(200, "api UserList")}func UserDelete(c *gin.Context) { c.String(200, "api UserDelete")}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 在
apiRouter.go
中调用userController.go
的函数
import ( "socket/controller/api" "github.com/gin-gonic/gin")func ApiRouter(r *gin.Engine) { apiRouter := r.Group("/api") { apiRouter.GET("/", ) apiRouter.GET("users", api.UserIndex) apiRouter.GET("users/:id", api.UserList) apiRouter.POST("users", api.UserAdd) apiRouter.PUT("users/:id", api.UserUpdate) apiRouter.DELETE("users/:id", api.UserDelete) }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 为了使方法能够继承,我们修改
userController.go
内容
package apiimport "github.com/gin-gonic/gin"type UserController struct {}func (con UserController) Index(c *gin.Context) { c.String(200, "api Index")}func (con UserController) Add(c *gin.Context) { c.String(200, "api Add")} func (con UserController) List(c *gin.Context) { c.String(200, "api List")}func (con UserController) Update(c *gin.Context) { c.String(200, "api update")}func (con UserController) Delete(c *gin.Context) { c.String(200, "api Delete")}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 继续修改
apiRouter.go
func ApiRouter(r *gin.Engine) { apiRouter := r.Group("/api") { apiRouter.GET("/") // api.UserController{} 实例化后在可以使用结构体的方法 apiRouter.GET("users", api.UserController{}.List) apiRouter.GET("users/:id", api.UserController{}.List) apiRouter.POST("users", api.UserController{}.Add) apiRouter.PUT("users/:id", api.UserController{}.Update) apiRouter.DELETE("users/:id", api.UserController{}.Delete) }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
8.2 控制器的继承
继承后就可以调用控制器里面的公共方法
- 在
api
目录下新建baseController.go
,内容如下:
package apiimport "github.com/gin-gonic/gin"type BaseController struct{}func (con BaseController) success(c *gin.Context) { c.String(200, "success")}func (con BaseController) error(c *gin.Context) { c.String(200, "failed")}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 继续修改
userController.go
type UserController struct { // 继承BaseController BaseController}func (con UserController) Index(c *gin.Context) { // c.String(200, "api Index") con.success(c)}func (con UserController) Add(c *gin.Context) { c.String(200, "api Add")}func (con UserController) List(c *gin.Context) { c.String(200, "api List")}func (con UserController) Update(c *gin.Context) { c.String(200, "api update")}func (con UserController) Delete(c *gin.Context) { c.String(200, "api Delete")}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
九、Gin 中间件
Gin 框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、 记录日志、耗时统计等
通俗的讲:中间件就是匹配路由前和匹配路由完成后执行的一系列操作
9.1 路由中间件
9.1.1 初识中间件
Gin 中的中间件必须是一个 gin.HandlerFunc 类型,配置路由的时候可以传递多个 func 回调函数。中间件要放在最后一个回调函数的前面 ,触发的方法都可以称为中间件
// 中间件函数func InitMiddleWare(c *gin.Context) { fmt.Println("init middle ware ")}func ApiRouter(r *gin.Engine) { apiRouter := r.Group("/api") { // 中间件要放在最后一个回调函数的前面 apiRouter.GET("/", InitMiddleWare, api.UserController{}.Index) }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
9.1.2 ctx.Next()调用该请求的剩余处理程序
中间件里面加上 ctx.Next()后,c.Next()的语句后面先不执行,跳转到后面的中间件和回调函数中执行完后,才执行c.Next()后面的语句
可以让我们在路由匹配完成后执行一些操作。比如我们统计一个请求的执行时间
func InitMiddleWare(c *gin.Context) { fmt.Println("1- init middle ware ") start := time.Now().UnixNano() // 调用c.Next()请求的剩余处理程序 // c.Next()的语句后面先不执行,先跳转路由匹配的最后一个回调函数执行后, // 才执行c.Next()后面的语句 c.Next() fmt.Println("3-程序执行完成 计算时间") // 计算耗时 Go 语言中的 Since()函数保留时间值,并用于评估与实际时间的差异 end := time.Now().UnixNano() fmt.Println(end - start)}func ApiRouter(r *gin.Engine) { apiRouter := r.Group("/api") { // 中间件要放在最后一个回调函数的前面 apiRouter.GET("/", InitMiddleWare, func(ctx *gin.Context) { fmt.Println("2 - 中间件") ctx.String(200, "/api") }) }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
9.1.3 一个路由配置多个中间件的执行顺序
- 多个中间件执行顺序
func InitMiddleWareOne(c *gin.Context) { fmt.Println("one- init middleware start") c.Next() fmt.Println("one- init middleware end")}func InitMiddleWareTwo(c *gin.Context) { fmt.Println("Two- init middleware start") c.Next() fmt.Println("Two- init middleware end")}func ApiRouter(r *gin.Engine) { apiRouter := r.Group("/api") { // 中间件要放在最后一个回调函数的前面 apiRouter.GET("/", InitMiddleWareOne, InitMiddleWareTwo, func(ctx *gin.Context) { fmt.Println("首页") ctx.String(200, "/api") }) }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 控制台输出的结果
one- init middleware startTwo- init middleware start首页Two- init middleware endone- init middleware end
- 1
- 2
- 3
- 4
- 5
9.1.4 ctx.Abort()了解
Abort是终止的意思,ctx.Abort()表示终止调用该请求的剩余处理程序
Abort()后,中间件后面的回调函数(包括后面的中间件)不执行了,直接执行该中间件这里面的语句
9.2 全局中间件
- r.Use(中间件1,中间件2…)
func InitMiddleWareOne(c *gin.Context) { fmt.Println("one- init middleware start") c.Next() fmt.Println("one- init middleware end")}func InitMiddleWareTwo(c *gin.Context) { fmt.Println("Two- init middleware start") c.Next() fmt.Println("Two- init middleware end")}func ApiRouter(r *gin.Engine) { apiRouter := r.Group("/api") apiRouter.Use(InitMiddleWareOne, InitMiddleWareTwo) { // 中间件要放在最后一个回调函数的前面 apiRouter.GET("/", func(ctx *gin.Context) { fmt.Println("首页") ctx.String(200, "/api") }) apiRouter.GET("users", api.UserController{}.Index) }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
9.3 在路由分组中配置中间件
9.4 中间件和对应控制器之间数据共享
-
中间件中设置值
ctx.Set("username", "张三")
-
控制器或者中间件中获取值
username, _ := ctx.Get("username")
-
username
是一个空接口类型,通过转为字符串v, ok := username.(string)
-
举例: 中间件
func LoginMiddleWare(c *gin.Context) { fmt.Println("login middle ware") c.Set("username", "张三")}
- 1
- 2
- 3
- 4
- 控制器
func (con UserController) Index(c *gin.Context) { username, _ := c.Get("username") fmt.Println(username) // c.String(200, username) c.JSON(200, gin.H{ "username" : username, }) // con.success(c)}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
中间件注意事项
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()
)
func LoginMiddleWare(c *gin.Context) { fmt.Println("login middle ware") c.Set("username", "张三") // 定义一个goroutine统计日志 cCp := c.Copy() go func () { time.Sleep(2 * time.Second) // 用了c.Request.URL.Path 也没有问题? fmt.Println("Done in path " + cCp.Request.URL.Path) }()}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
十、Gin 中自定义
10.1 关于Model
如果我们的应用非常简单的话,我们可以在Controller 里面处理常见的业务逻辑。但是如果
我们有一个功能想在多个控制器、或者多个模板里面复用的话,那么我们就可以把公共的功
能单独抽取出来作为一个模块(Model)。Model 是逐步抽象的过程,一般我们会在Model
里面封装一些公共的方法让不同Controller 使用,也可以在Model 中实现和数据库打交道
10.2 Model 里面封装公共的方法
- 新建
model/tools.go
package modelimport ( "crypto/md5" "fmt" "time")//时间戳间戳转换成日期func UnixToDate(timestamp int) string { t := time.Unix(int64(timestamp), 0) return t.Format("2006-01-02 15:04:05")}//日期转换成时间戳2020-05-02 15:04:05func DateToUnix(str string) int64 { template := "2006-01-02 15:04:05" t, err := time.ParseInLocation(template, str, time.Local) if err != nil { return 0 } return t.Unix()}// 获取时间戳func GetUnix() int64 { return time.Now().Unix()}// 获取当前日期func GetDate() string { template := "2006-01-02 15:04:05" return time.Now().Format(template)}// 获取年月日func GetDay() string { template := "20060102" return time.Now().Format(template)}func Md5(str string) string { data := []byte(str) return fmt.Sprintf("%x\", md5.Sum(data))}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
十一、Gin 文件上传
十二、Gin 中的Cookie
12.1 Cookie 介绍
- HTTP 是无状态协议。简单地说,当你浏览了一个页面,然后转到同一个网站的另一个页
面,服务器无法认识到这是同一个浏览器在访问同一个网站。每一次的访问,都是没有任何
关系的。如果我们要实现多个页面之间共享数据的话我们就可以使用Cookie 或者Session 实
现 - cookie 是存储于访问者计算机的浏览器中。可以让我们用同一个浏览器访问同一个域名
的时候共享数
12.2 Cookie 能实现的功能
- 保持用户登录状态
- 保存用户浏览的历史记录
- 猜你喜欢,智能推荐
- 电商网站的加入购物车
12.3 设置和获取Cookie
- 前面我们已经使用过
ctx.Set("username")
和ctx.Get("username")
来进行数据的保存和共享,但这个使用的只针对是单页面的数据共享,要想实现多页面的共享,就需要Cookie
或者Session
12.3.1 设置Cookie
c.SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool)
其中:
* 第一个参数key
* 第二个参数value
* 第三个参数过期时间.如果只想设置Cookie 的保存路径而不想设置存活时间,可以在第三个参数中传递nil
* 第四个参数cookie 的路径
* 第五个参数cookie 的路径Domain 作用域本地调试配置成localhost , 正式上线配置成域名
* 第六个参数是secure ,当secure 值为true 时,cookie 在HTTP 中是无效,在HTTPS 中才有效
* 第七个参数httpOnly,是微软对COOKIE 做的扩展。如果在COOKIE 中设置了“httpOnly”属性,则通过程序(JS 脚本、applet 等)将无法读取到COOKIE 信息,防止XSS 攻击产生
12.3.2 获取Cookie
cookie, err := c.Cookie("name")
12.3.3 删除Cookie
- 把第三个参数时间设置为
-1
12.3.3 举例
func ApiRouter(r *gin.Engine) { apiRouter := r.Group("/api") { apiRouter.GET("/", func(ctx *gin.Context) { // 设置Cookie ctx.SetCookie("username", "张三", 3600, "/", "localhost", false, false) fmt.Println("首页") ctx.String(200, "/api") }) apiRouter.GET("/news", func(ctx *gin.Context) { // 获取Cookie username, _ := ctx.Cookie("username") fmt.Println(username) ctx.String(200, "/news/"+username) }) }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
12.4 多个二级域名共享cookie
- 我们想的是用户在
a.test.com
中设置Cookie 信息后在b.test.com
中获取刚才设置的cookie,也就是实现多个二级域名共享cookie, 这时候的话我们就可以这样设置cookie
c.SetCookie("usrename", "张三", 3600, "/", ".test.com", false, true)