Go Web框架之Gin(3)

回顾

上一次,我们讲解了如何在 Gin 框架中使用静态文件,控制器,路由分组以及中间件相关等。

Model

如果我们的应用非常简单的话,我们可以在 Controller 里面处理常见的业务逻辑。但是如果我们有一个功能想在多个控制器、或者多个模板里面复用的话,那么我们就可以把公共的功能单独抽取出来作为一个模块(Model)。 Model 是逐步抽象的过程,一般我们会在 Model 里面封装一些公共的方法让不同 Controller 使用,也可以在 Model 中实现和数据库打交道。

封装方法

新建 models/utils.go

package models

import (
	"fmt"
	"time"
)

// TimeStamp2Date 时间戳转换日期
func TimeStamp2Date(timeStamp int64) string {
	t := time.Unix(int64(timeStamp), 0)
	return t.Format("2006-01-02 15:04:05")
}

// Date2TimeStamp 日期转换时间戳
func Date2TimeStamp(date string) int64 {
	template := "2006-01-02 15:04:05"
	t, err := time.ParseInLocation(template, date, time.Local)
	if err != nil {
		return 0
	}
	return t.Unix()
}

// GetTimeStamp 获取当前时间戳
func GetTimeStamp() int64 {
	return time.Now().Unix()
}

控制器调用 Model

import "ginpro/ginmodel/models"

dataStr := models.TimeStamp2Date(str)

文件上传

单文件上传

定义模板文件

需要在上传文件的 form 表单上面需要加入 enctype="multipart/form-data"

{{define "template/index.html"}}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h2>文件提交演示</h2>
<form action="/upload" method="post" enctype="multipart/form-data">
    用户名:<input type="text" name="username" paceholder="username">
    <br>
    头像:<input type="file" name="face">
    <br>
    <input type="submit" value="提交">
</form>
</body>
</html>
{{end}}

实现业务代码

r.LoadHTMLGlob("templates/*")

r.GET("/login", func(c *gin.Context) {
	c.HTML(200, "template/index.html", gin.H{})
})

r.POST("/upload", func(c *gin.Context) {
	username := c.PostForm("username")

	file, err := c.FormFile("face")
	dst := path.Join("./static/upload", file.Filename)
	if err == nil {
		c.SaveUploadedFile(file, dst)
	}
	c.JSON(200, gin.H{
		"success":  true,
		"username": username,
		"dst":      dst,
	})
})

多文件上传-不同名字的多文件

定义模板

{{define "templates/default.html"}}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>多文件上传展示</h1>
<form action="/uploadfiles" method="post" enctype="multipart/form-data">
  用户名:<input type="text" name="username" paceholder="username">
  <br>
  头像1:<input type="file" name="face1">
  <br>
  头像2:<input type="file" name="face2">
  <input type="submit" value="上传">
</form>
</body>
</html>
{{end}}

实现业务代码

r.GET("/any", func(c *gin.Context) {
		c.HTML(200, "templates/default.html", gin.H{})
	})

r.POST("/uploadfiles", func(c *gin.Context) {
	username := c.PostForm("username")

	file1, err := c.FormFile("face1")
	dst := path.Join("./static/upload", file1.Filename)
	if err == nil {
		c.SaveUploadedFile(file1, dst)
	}
	file2, err := c.FormFile("face2")
	dst1 := path.Join("./static/upload", file2.Filename)
	if err == nil {
		c.SaveUploadedFile(file1, dst1)
	}
	c.JSON(200, gin.H{
		"success":  true,
		"username": username,
		"dst":      dst,
		"dst1":     dst1,
	})
})

多文件上传-相同名字的多文件

定义模板

{{define "templates/muti.html"}}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>多文件上传展示</h1>
<form action="/uploadmuti" method="post" enctype="multipart/form-data">
    用户名:<input type="text" name="username" paceholder="username">
    <br>
    头像1:<input type="file" name="face[]">
    <br>
    头像2:<input type="file" name="face[]">
    <input type="submit" value="上传">
</form>
</body>
</html>
{{end}}

实现业务代码

r.GET("/muti", func(c *gin.Context) {
		c.HTML(200, "templates/muti.html", gin.H{})
	})

r.POST("/uploadmuti", func(c *gin.Context) {
	username := c.PostForm("username")

	form, _ := c.MultipartForm()
	files := form.File["face[]"]

	for _, file := range files {
		// 传文件指定目录
		dst := path.Join("./static/upload", file.Filename)
		c.SaveUploadedFile(file, dst)
	}

	c.JSON(200, gin.H{
		"success":  true,
		"username": username,
	})
})

Gin 中的 Cookie

介绍

HTTP 是一种无状态的协议,用于在网络上传输数据。无状态意味着每个请求都是独立的,服务器不会在两个请求之间保持任何信息。当你在浏览器中输入一个 URL 并按下 Enter 键时,浏览器就会发送一个 HTTP 请求到服务器,然后服务器会返回一个 HTTP 响应。HTTP 请求和响应可以包含各种类型的数据,比如 HTML 文档,图片,视频等。

Cookie,另一方面,是一种可以让 HTTP 变得“有状态”的技术。当你首次访问一个网站时,服务器可能会在 HTTP 响应中包含一个 Set-Cookie 头,这个头会告诉浏览器创建一个 Cookie 并保存一些信息。然后,当你再次访问同一个网站时,浏览器会在 HTTP 请求中包含一个 Cookie 头,将之前保存的 Cookie 发送回服务器。

通过 Cookie,服务器可以在多个请求之间记住用户的信息。比如,服务器可以在 Cookie 中保存用户的登录状态,这样用户就不需要在每次请求时都重新登录。或者,服务器可以在 Cookie 中保存用户的购物车信息,这样用户可以在多个页面之间添加商品到购物车,然后在结账页面一次性购买所有商品。

功能

  • 会话管理:Cookie 常用于记录用户是否已经登录,这样用户在浏览不同的页面或者在下次访问网站时,不需要重复登录。此外,Cookie 也可以用于保存用户在电商网站的购物车信息,即使用户关闭浏览器再重新打开,购物车里的商品也不会丢失。
  • 个性化设置:Cookie 可以保存用户的偏好设置,如主题颜色、语言、字体大小等,提供个性化的用户体验。
  • 跟踪和分析:通过在 Cookie 中记录用户的浏览行为(如访问过哪些页面,点击过哪些链接等),网站可以进行用户行为分析,以改进服务或提供更精准的广告推送。
  • 状态保持:由于 HTTP 协议本身是无状态的,Cookie 可以帮助维护服务器和客户端之间的状态信息。这使得服务器能够记住用户的操作,例如填写的表单信息,浏览的页面等。
  • 安全性:Cookie 也可以用于增强安全性,例如防止跨站请求伪造(CSRF)攻击。在这种情况下,服务器会在 Cookie 中设置一个独特的、随机的标记,并要求每个后续的请求都包含这个标记。

操作 Cookie

使用 SetCookie 方法对响应设置 Cookie 。

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,
		Value:    url.QueryEscape(value),
		MaxAge:   maxAge,
		Path:     path,
		Domain:   domain,
		SameSite: c.sameSite,
		Secure:   secure,
		HttpOnly: httpOnly,
	})
}

参数解释:

  • Name:Cookie 的名称。这个字段是必须的,它的值可以在浏览器的 Cookie 存储中唯一标识这个 Cookie 。
  • Value:Cookie 的值。这是存储在 Cookie 中的实际信息,可以是任何字符串。
  • Path:Cookie 适用的路径。如果设置为"/",那么这个 Cookie 在整个网站上都是有效的。如果设置为特定的路径,比如 "/pages" ,那么这个 Cookie 只在 "/pages" 这个路径及其子路径下有效。
  • Domain:Cookie 适用的域名。如果没有设置,那么默认为设置这个 Cookie 的网站的域名。
  • Expires:Cookie 的过期时间。这是一个 time.Time 类型的值,表示这个 Cookie 何时过期。如果不设置,或者设置为零值,那么这个 Cookie 会在浏览器关闭时过期。
  • MaxAge:以秒为单位的到期时间。如果设置了 MaxAge,那么 Expires 字段会被忽略。如果 MaxAge<=0 ,那么 Cookie 会在浏览器关闭时过期。
  • Secure:这个字段如果设为 true ,表示这个 Cookie 只能通过 HTTPS 协议传输,不能通过非加密的 HTTP 协议传输。这可以防止Cookie被网络中间人窃取。
  • HttpOnly:这个字段如果设为 true ,表示这个 Cookie 不能被 JavaScript 的 Document.cookie API 访问,只能通过 HTTP(S) 传输。这可以防止 Cookie 被跨站脚本攻击(XSS)窃取。

使用Cookie()来获取 Cookie。

func (c *Context) Cookie(name string) (string, error) {
	cookie, err := c.Request.Cookie(name)
	if err != nil {
		return "", err
	}
	val, _ := url.QueryUnescape(cookie.Value)
	return val, nil
}

实例:

func (con DefaultController) SET(c *gin.Context) {
	c.SetCookie("username", "李四", 3600, "/", "localhost", false, true)
	c.SetCookie("age", "18", 10, "/", "localhost", false, true)
	c.HTML(http.StatusOK, "templates/index.html", gin.H{
		"msg": "我是一个msg",
		"t":   1629788010,
	})
}

func (con DefaultController) GET(c *gin.Context) {
	//获取cookie
	username, _ := c.Cookie("username")
	age, _ := c.Cookie("age")
	c.String(http.StatusOK, "用户--cookie.username="+username)
	c.String(http.StatusOK, "用户--cookie.age="+age)
}

// DeleteCookie 删除cookie
func (con DefaultController) DeleteCookie(c *gin.Context) {
	c.SetCookie("username", "李四", -1, "/", "127.0.0.1", false, true)
}

Gin 中的 Session

Session 是在服务器端维护的一种持久化状态信息,它可以跟踪用户的活动。当用户第一次访问服务器时,服务器会为用户创建一个唯一的 Session ID,并在后续的每次请求中,服务器都可以通过这个 Session ID 来识别用户。服务器通常会在内存中或者数据库中存储与 Session ID 关联的数据,比如用户的登录状态,购物车信息等。

而 Cookie 则是在客户端(即用户的浏览器)保存状态信息的一种方式。当服务器发送 HTTP 响应时,可以附带一些 Cookie ,浏览器会保存这些 Cookie ,并在后续的每次请求中把这些 Cookie 发送回服务器。

Cookie 和 Session 的主要区别是什么呢?

  • 存储位置:Cookie 存储在客户端,而 Session 存储在服务器端。这意味着 Cookie 可能更容易被用户修改,而 Session 则相对更安全。
  • 生命周期:默认情况下,Cookie 的生命周期由其 Expires 或 Max-Age 属性决定,而 Session 的生命周期通常由服务器设置,比如在用户一段时间内没有活动后,Session 会被自动销毁。
  • 容量限制:由于 Cookie 需要在每次 HTTP 请求和响应中传递,所以其大小有限制,通常不能超过 4KB 。而 Session 则没有这个限制,因为只需要传递 Session ID。
  • 跨域问题:Cookie 可以在同源策略下跨子域名共享,而 Session 由于是基于 Cookie 实现的,所以也受到同源策略的限制。如果需要跨域共享状态,可能需要使用其他技术,比如JSONP,CORS,postMessage等。
  • 使用场景:由于 Cookie 直接存储在客户端,所以适用于存储一些不太敏感的信息,比如网站的语言设置。而 Session 则更适用于存储敏感信息,比如用户的登录状态。

基于 Cookie 存储 Session

安装 session 包

go get github.com/gin-contrib/sessions

基本的用法

// 创建一个基于 cookie 的 session store
// secret  密钥
store := cookie.NewStore([]byte("secret111111"))

//设置session中间件,参数my_session,指的是session的名字,也是cookie的名字
//store是前面创建的存储引擎,我们可以替换成其他存储引擎
r.Use(sessions.Sessions("my_session", store))

编写控制器

type DefaultController struct {
}

// SetSession set session
func (con DefaultController) SetSession(c *gin.Context) {
	// set session
	session := sessions.Default(c)

	//设置过期时间
	session.Options(sessions.Options{
		MaxAge: 3600 * 6, //6hrs
	})

	session.Set("name", "lemon")
	session.Save()

	c.String(200, "set session name lemon")
}

// GetSession get session
func (con DefaultController) GetSession(c *gin.Context) {
	// 初始化session
	session := sessions.Default(c)
	name := session.Get("name")
	c.String(200, "get session name %s", name)
}

在路由中使用

r.GET("/set", controller.DefaultController{}.SetSession, func(c *gin.Context) {
		c.String(200, "set ok")
})
r.GET("/get", controller.DefaultController{}.GetSession, func(c *gin.Context) {
	c.String(200, "get ok")
})

基于 Redis 存储 Session

安装 redis 包

go get github.com/gin-contrib/sessions/redis

基本使用

//初始化基于redis的存储引擎: 需要启动redis服务,不然会报错
	//参数说明:
	//自第1个参数-redis最大的空闲连接数
	//第2个参数-数通信协议tcp或者udp
	//第3个参数-redis地址,格式,host:port 第4个参数-redis密码
	//第5个参数-session加密密钥
	store, _ := redis.NewStore(10, "tcp", "localhost:6379", "", []byte("secret"))

我们只用修改存储引擎即可,其他略同。

总结

到此为止,关于 Gin 的基本使用,我们已经讲解完毕,之后还有更多的有关 go 的教程。

  • 22
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值