Go --- gin基础知识点


# 使用gin的理由
  • Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确,具有快速灵活,容错方便等特点
  • 对于golang而言,web框架的依赖要远比Python,Java之类的要小。自身的net/http足够简单,性能也非常不错
  • 借助框架开发,不仅可以省去很多常用的封装带来的时间,也有助于团队的编码风格和形成规范

简单使用

安装

go get -u github.com/gin-gonic/gin

导入

import “github.com/gin-gonic/gin”

hello word示例

package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

func main(){
    // 1.创建路由
    r := gin.Default()
    // 2.绑定路由规则,执行的函数
    // gin.Context,封装了request和response
    r.GET("/", func(c *gin.Context) {
        c.String(http.StatusOK, "hello World!")
    })
    // 3.监听端口,默认在8080
    // Run("里面不指定端口号默认为8080")
    // 指定端口避免:8080这个常用端口被占用
    r.Run(":8000")
}

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p8wS9rUN-1643199441416)(attachment:dfe6004f7535e751d9ea88265771f5a2)]

gin路由

基本路由

  • gin 框架中采用的路由库是基于httprouter做的
  • 因为虽然net/http这个包里有着默认路由,但是仍存在着不足,所以使用httprouter
  • httprouter 是一个高性能、可扩展的HTTP路由,上面我们列举的net/http默认路由的不足,都被httprouter 实现
  • 要想了解更多的有关httprouter的知识,请访问:Git仓库地址

API

  • 使用Restful风格的API(URL定位资源,用HTTP描述操作)

    1.获取文章 /blog/getXxx Get blog/Xxx

    2.添加 /blog/addXxx POST blog/Xxx

    3.修改 /blog/updateXxx PUT blog/Xxx

    4.删除 /blog/delXxxx DELETE blog/Xxx

参数获取

API参数

  • 通过Context的Param方法来获取API参数

    示例:

    package main
    
    import (
        "github.com/gin-gonic/gin"
        "net/http"
        "strings"
    )
    
    func main() {
        r := gin.Default()
        r.GET("/user/:name/*action", func(c *gin.Context) {
            name := c.Param("name")
            action := c.Param("action")
            //c.String(http.StatusOK,"name = "+name+"action = "+action)
            //截取/
            action = strings.Trim(action, "/")
            c.String(http.StatusOK, name+" is "+action)
        })
        //默认为监听8080端口
        r.Run(":8000")
    }
    

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xznNphwp-1643199441417)(attachment:5809ce9ca0f59f38b5c4d4dc866eb89c)]

url参数

  • URL参数可以通过DefaultQuery()或Query()方法获取
  • DefaultQuery()若参数不村则,返回默认值,Query()若不存在,返回空串

示例:

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "net/http"
)

func main() {
    r := gin.Default()
    r.GET("/user", func(c *gin.Context) {
        //不指定默认值,若是不传参则接受的是空串
        //name := c.Query("name")
        //指定默认值
        //http://localhost:8080/user 才会打印出来默认的值
        name := c.DefaultQuery("name", "Re")
        c.String(http.StatusOK, fmt.Sprintf("hello %s", name))
    })
    r.Run(":8000")
}

不传参

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oMbLIIrB-1643199441417)(attachment:f1386b28bad5c79af42405a8404f9048)]****

传参

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v1obcmOT-1643199441424)(attachment:1046057b3377310bb9db1db49aa3ba11)]

表单参数

  • 表单传输为post请求,http常见的传输格式为四种:
    1. application/json
    2. application/x-www-form-urlencoded
    3. application/xml
    4. multipart/form-data
  • 表单参数可以通过PostForm()方法获取,该方法默认解析的是x-www-form-urlencoded或from-data格式的参数

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>提交页面</title>
</head>
<body>
    <form action="http://localhost:8000/form" method="post" action="application/x-www-form-urlencoded">
        用户名:<input type="text" name="username" placeholder="请输入你的用户名"> <br>
        密&nbsp码:<input type="password" name="password" placeholder="请输入你的用户名">
        <input type="submit" value="提交">
    </form>
</body>
</html>
package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "net/http"
)

func main() {
    r := gin.Default()
    r.POST("/form", func(c *gin.Context) {
        // 获取/form的请求类型,及各参数
        types := c.DefaultPostForm("type", "post")
        username := c.PostForm("username")
        password := c.PostForm("password")
        c.String(http.StatusOK, fmt.Sprintf("username:%s,password:%s,type:%s", username, password, types))
    })
    r.Run(":8000")
}

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EKj0AxIi-1643199441425)(attachment:65df7f3f4086461d50e91b01ec0ad879)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DplZcDsW-1643199441426)(attachment:fa61131e9c41b3a0c6b8201626d8c616)]

上传文件

  • multipart/form-data格式用于文件上传
  • gin文件上传与原生的net/http方法类似,不同在于gin把原生的request封装到c.Request中

单个文件

  • 使用 Request.FormFile 方法获取文件

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>上传单个文件</title>
</head>
<body>
    <form action="http://localhost:8000/upload" method="post" enctype="multipart/form-data">
      上传文件:<input type="file" name="file" >
      <input type="submit" value="提交">
    </form>
</body>
</html>
package main

import (
    "github.com/gin-gonic/gin"
    "log"
    "net/http"
)

// 上传文件
func main() {
    r := gin.Default()
    //限制上传最大尺寸
    r.MaxMultipartMemory = 8 << 20
    r.POST("/upload", func(c *gin.Context) {
        _, headers, err := c.Request.FormFile("file")
        if err != nil {
            log.Printf("Error when try to get file: %v", err)
        }
        //上传特定的文件
        //headers.Size 获取文件大小
        //if headers.Size > 1024*1024*2 {
        //    fmt.Println("文件太大了")
        //    return
        //}
        //headers.Header.Get("Content-Type")获取上传文件的类型
        //if headers.Header.Get("Content-Type") != "image/png" {
        //    fmt.Println("只允许上传png图片")
        //    return
        //}
        c.SaveUploadedFile(headers, "./video/"+headers.Filename)
        c.String(http.StatusOK, "上传成功" + headers.Filename)
    })
    r.Run(":8000")

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n3hPrmWk-1643199441427)(attachment:fde54c89bffc027383a528b67495c362)]

多个文件

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>上传多个文件</title>
</head>
<body>
<form action="http://localhost:8000/upload" method="post" enctype="multipart/form-data">
  上传文件:<input type="file" name="files" multiple>
  <input type="submit" value="提交">
</form>
</body>
</html>
func main() {
    // 1.创建路由
    // 默认使用了2个中间件Logger(), Recovery()
    r := gin.Default()
    // 限制表单上传大小 8MB,默认为32MB
    r.MaxMultipartMemory = 8 << 20
    r.POST("/upload", func(c *gin.Context) {
        form, err := c.MultipartForm()
        if err != nil {
            c.String(http.StatusBadRequest, fmt.Sprintf("get err %s", err.Error()))
        }
        // 获取所有图片
        files := form.File["files"]
        // 遍历所有图片
        for _, file := range files {
            // 逐个存
            if err := c.SaveUploadedFile(file, file.Filename); err != nil {
                c.String(http.StatusBadRequest, fmt.Sprintf("upload err %s", err.Error()))
                return
            }
        }
        c.String(200, fmt.Sprintf("upload ok %d files", len(files)))
    })
    //默认端口号是8080
    r.Run(":8000")
}

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OzSl8Caf-1643199441427)(attachment:bdeeb060943b97690f80bc5f0ea89a91)]

路由组

  • 用来管理有相同的URL的路由

使用示例:

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
)

// 路由组
func main() {
    // 1.创建路由
    // 默认使用了2个中间件Logger(), Recovery()
    r := gin.Default()
    // 路由组1 ,处理GET请求
    v1 := r.Group("/v1")
    // {} 是书写规范
    {
        v1.GET("/login", login)
        v1.GET("submit", submit)
    }
    v2 := r.Group("/v2")
    {
        v2.POST("/login", login)
        v2.POST("/submit", submit)
    }
    r.Run(":8000")
}

func login(c *gin.Context) {
    name := c.DefaultQuery("name", "jack")
    c.String(200, fmt.Sprintf("hello %s\n", name))
}

func submit(c *gin.Context) {
    name := c.DefaultQuery("name", "lily")
    c.String(200, fmt.Sprintf("hello %s\n", name))
}

访问失败页面

使用r.NoRoute来设置

例如:

r.NoRoute(func(c *gin.Context) {
        c.String(http.StatusNotFound, "sorry,天太冷了,页面跑去钻小被窝了")
})

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bIHtJtFg-1643199441428)(attachment:6d94e36fdfa86b7238b5042a2334423a)]

gin路由原理

gin数据解析和绑定

Json数据解析和绑定

  • 客户端传参,Json格式,服务端解析到结构体
  • 使用gin.Context.ShouldBindJSON(&json) err将接收到的json数据解析到结构体中
User    string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"`
Pssword string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"`
  • 解析中binding中的required该字段表示必须传参,如果不传参则会报错

表单数据解析和绑定

  • 使用gin.Context.Bind(&form) err 将接收到的表单数据解析到结构体中
··<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>表单提交数据解析到结构体</title>
</head>
<body>
    <form action="http://localhost:8000/loginForm" method="post" enctype="application/x-www-form-urlencoded">
    用户名:<input type="text" name="username"><br>
    密&nbsp码:<input type="password" name="password"><br>
    <input type="submit" value="提交">
    </form>
</body>
</html>
package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

type Login struct {
    // binding:"required"修饰的字段,若接收为空值,则报错,是必须字段
    User    string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"`
    Pssword string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"`
}

func main() {
    // 1.创建路由
    // 默认使用了2个中间件Logger(), Recovery()
    r := gin.Default()
    // JSON绑定
    r.POST("/loginForm", func(c *gin.Context) {
        // 声明接收的变量
        var form Login
        // Bind()默认解析并绑定form格式
        // 根据请求头中content-type自动推断
        if err := c.Bind(&form); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }
        // 判断用户名密码是否正确
        if form.User != "root" || form.Pssword != "admin" {
            c.JSON(http.StatusBadRequest, gin.H{"status": http.StatusBadRequest})
            return
        }
        c.JSON(http.StatusOK, gin.H{"status": http.StatusOK,"body":form})
    })
    r.Run(":8000")
}

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oG0DC7yf-1643199441428)(attachment:fb20260795f9b78599b063953680355a)]

URI数据解析和绑定

  • 使用gin.Context.ShouldBindUri(&login) err 解析uri数据到结构体中

示例:

func main() {
    // 1.创建路由
    // 默认使用了2个中间件Logger(), Recovery()
    r := gin.Default()
    // JSON绑定
    r.GET("/:user/:password", func(c *gin.Context) {
        // 声明接收的变量
        var login Login
        // Bind()默认解析并绑定form格式
        // 根据请求头中content-type自动推断
        if err := c.ShouldBindUri(&login); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }
        // 判断用户名密码是否正确
        if login.User != "root" || login.Pssword != "admin" {
            c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
            return
        }
        c.JSON(http.StatusOK, gin.H{"status": "200"})
    })
    r.Run(":8000")
}

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LA6JX5ia-1643199441428)(attachment:e6efba1a4e1310a6d180619aad33bfd6)]

gin 渲染

各种数据格式的响应

  • json、结构体、XML、YAML类似于java的properties、ProtoBuf
func main() {
    // 1.创建路由
    // 默认使用了2个中间件Logger(), Recovery()
    r := gin.Default()
    // 1.json
    r.GET("/someJSON", func(c *gin.Context) {
        c.JSON(200, gin.H{"message": "someJSON", "status": http.StatusOK})
    })
    // 2. 结构体响应
    r.GET("/someStruct", func(c *gin.Context) {
        var msg struct {
            Name    string
            Message string
            Number  int
        }
        msg.Name = "root"
        msg.Message = "message"
        msg.Number = 123
        c.JSON(http.StatusOK, msg)
    })
    // 3.XML
    r.GET("/someXML", func(c *gin.Context) {
        c.XML(200, gin.H{"message": "abc"})
    })
    // 4.YAML响应
    r.GET("/someYAML", func(c *gin.Context) {
        c.YAML(200, gin.H{"name": "zhangsan"})
    })
    // 5.protobuf格式,谷歌开发的高效存储读取的工具
    // 数组?切片?如果自己构建一个传输格式,应该是什么格式?
    //r.GET("/someProtoBuf", func(c *gin.Context) {
    //    reps := []int64{int64(1), int64(2)}
    //    // 定义数据
    //    label := "label"
    //    // 传protobuf格式数据
    //    data := &protoexample.Test{
    //        Label: &label,
    //        Reps:  reps,
    //    }
    //    c.ProtoBuf(200, data)
    //})

    r.Run(":8000")
}

XML页面结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yKstASoJ-1643199441429)(attachment:dd512d64675ada67c5a7cc276ef2ab88)]

HTML模板渲染

  • gin支持加载HTML模板, 然后根据模板参数进行配置并返回相应的数据,本质上就是字符串替换
  • LoadHTMLGlob()方法可以加载模板文件

html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{.title}}</title>
</head>
<body>
    <h1>{{.test}}</h1>
</body>
</html>

go文件:

package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

// html模板渲染
func main() {
    r := gin.Default()
    // 加载模板文件
    r.LoadHTMLGlob("static/*")
    // 如果项目结构不同,也可以是
    // r.LoadHTMLGlob("static/**/*")
    r.GET("/index", func(c *gin.Context) {
        c.HTML(http.StatusOK, "index.html", gin.H{"title": "我是测试", "test": "这是一个测试文件"})
    })
    r.Run(":8000")
}

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mc57rGxu-1643199441429)(attachment:197a1098605a63ed0703f6869d539df8)]

重定向

  • 使用 gin.Context.Redirect 进行重定向
package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

// 重定向
func main() {
    r := gin.Default()
    r.GET("/index", func(c *gin.Context) {
        c.Redirect(http.StatusMovedPermanently, "http://www.baidu.com")
    })
    r.Run(":8000")
}

同步异步

  • 同步:先执行再响应
  • 异步:先响应再执行
  • go的协程机制(goroutine)可以方便地实现异步处理
  • 另外,在启动新的goroutine时,不应该使用原始上下文,必须使用它的只读副本
package main

import (
    "github.com/gin-gonic/gin"
    "log"
    "time"
)

// 同步异步
func main() {
    // 1.创建路由
    // 默认使用了2个中间件Logger(), Recovery()
    r := gin.Default()
    // 1.异步
    r.GET("/long_async", func(c *gin.Context) {
        // 需要搞一个副本
        copyContext := c.Copy()
        // 异步处理
        go func() {
            time.Sleep(30 * time.Second)
            log.Println("异步执行:" + copyContext.Request.URL.Path)
        }()
    })
    // 2.同步
    r.GET("/long_sync", func(c *gin.Context) {
        time.Sleep(30 * time.Second)
        log.Println("同步执行:" + c.Request.URL.Path)
    })

    r.Run(":8000")
}

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9cSpqCDL-1643199441430)(attachment:8ec8d8010b6371bb9c0a99a460eff658)]

gin 中间件

全局中间件

  • 所有请求都需要经过的中间件
    package main
    
    import (
        "fmt"
        "github.com/gin-gonic/gin"
    )
    
    // 先定义一个中间件
    func MiddleWare() gin.HandlerFunc {
        return func(c *gin.Context) {
            fmt.Println("中间件开始执行了")
            // 设置变量到Context的key中,可以通过Get()取
            c.Set("request", "中间件")
            status := c.Writer.Status()
            fmt.Println("中间件执行完毕", status)
        }
    }
    
    func main() {
        // 1.创建路由
        // 默认使用了2个中间件Logger(), Recovery()
        r := gin.Default()
        // 注册中间件
        r.Use(MiddleWare())
        // {}为代码规范
        {
            r.GET("/test", func(c *gin.Context) {
                // 取值
                req, _ := c.Get("request")
                fmt.Println("request:", req)
                // 页面接收
                c.JSON(200, gin.H{"request": req})
            })
        }
        r.Run(":8000")
    }
    

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UjH27hOU-1643199441431)(attachment:40a6217e254239bd67e716e88a4186e7)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6CuC9tS2-1643199441431)(attachment:1eebd7253d1503854ebe5c29944b21da)]

Next() 函数


源码:

// Next should be used only inside middleware.
// Next 应该只在中间件中被使用
// It executes the pending handlers in the chain inside the calling handler.
// 挂起现在正在执行的handlers
// See example in GitHub.
func (c *Context) Next() {
    c.index++
    for s := int8(len(c.handlers)); c.index < s; c.index++ {
        c.handlers[c.index](c)
    }
}

测试:

// 中间件中Next的函数的测试
func main() {
	r := gin.New()

	mid1 := func(c *gin.Context) {
		start := time.Now()
		fmt.Println("middleware1 start")

		// 注释 or 不注释,查看输入结果
		//c.Next()

		fmt.Println(time.Since(start))
		fmt.Println("middleware1 ending")
	}

	mid2 := func(c *gin.Context) {
		fmt.Println("middleware2 start")
		c.Next()
		fmt.Println("middleware2 ending")
	}

	r.Use(mid1)
	r.Use(mid2)
	r.GET("/", func(c *gin.Context) {
		c.JSON(http.StatusOK, "hi")
	})
	r.Run(":8000")
}

结果:

# mid1中的Next()执行
middleware1 start
middleware2 start
middleware2 ending
758.6µs
middleware1 ending
# mid1中的Next()注释上
middleware1 start
# 不加Next则不会出现响应时间
0s
middleware1 ending
middleware2 start
middleware2 ending

可以看出,再加Next函数后mid1会先挂起,等其余的中间件(mid2)执行完再继续执行Next函数后的语句,并且这个过程是一个压栈的过程,也就是说先执行的Next后的语句后被执行。

Next之前的操作一般用来做验证处理,访问是否允许之类的。
Next之后的操作一般是用来做总结处理,比如格式化输出、响应结束时间,响应时长计算之类的。

局部中间件

  • 在特殊的路由中指定中间件

示例:

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
)

func MiddleWare() gin.HandlerFunc {
	return func(c *gin.Context) {
		t := time.Now()
		fmt.Println("中间件开始执行了")
		// 设置变量到Context的key中,可以通过Get()取
		c.Set("request", "中间件")
		// 执行函数
		c.Next()
		// 中间件执行完后续的一些事情
		status := c.Writer.Status()
		fmt.Println("中间件执行完毕", status)
		t2 := time.Since(t)
		fmt.Println("time:", t2)
	}
}

func main() {
	// 1.创建路由
	// 默认使用了2个中间件Logger(), Recovery()
	r := gin.Default()
	//局部中间键使用
	r.GET("/ce", MiddleWare(), func(c *gin.Context) {
		// 取值
		req, _ := c.Get("request")
		fmt.Println("request:", req)
		// 页面接收
		c.JSON(200, gin.H{"request": req})
	})
	r.Run(":8000")
}

结果:

只有在访问"/ce"会执行中间件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aJk5hBir-1643199441432)(attachment:e269ce4f3f338da384af1f3bbe6867e8)]

会话处理

Cookie

Cookie介绍

  • HTTP是无状态协议,服务器不能记录浏览器的访问状态,也就是说服务器不能区分两次请求是否由同一个客户端发出
  • Cookie就是解决HTTP协议无状态的方案之一,中文是小甜饼的意思
  • Cookie实际上就是服务器保存在浏览器上的一段信息。浏览器有了Cookie之后,每次向服务器发送请求时都会同时将该信息发送给服务器,服务器收到请求后,就可以根据该信息处理请求
  • Cookie由服务器创建,并发送给浏览器,最终由浏览器保存

cookie用途

  • 测试服务端发送cookie给客户端,客户端请求时携带cookie

示例:

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
)

func main() {
	// 1.创建路由
	// 默认使用了2个中间件Logger(), Recovery()
	r := gin.Default()
	// 服务端要给客户端cookie
	r.GET("cookie", func(c *gin.Context) {
		// 获取客户端是否携带cookie
		cookie, err := c.Cookie("key_cookie")
		if err != nil {
			cookie = "NotSet"
			// 给客户端设置cookie
			//  maxAge int, 单位为秒
			// path,cookie所在目录
			// domain string,域名
			//   secure 是否智能通过https访问
			// httpOnly bool  是否允许别人通过js获取自己的cookie
			c.SetCookie("key_cookie", "value_cookie", 60, "/",
				"localhost", false, true)
		}
		fmt.Printf("cookie的值是: %s\n", cookie)
	})
	r.Run(":8000")
}
// 其中 SetCookie方法的参数的含义为
// maxAge int, 单位为秒
// path,cookie所在目录
// domain string,域名
// secure 是否智能通过https访问
// httpOnly bool  是否允许别人通过js获取自己的cookie
SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool)

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U41boFGY-1643199441432)(attachment:4492e74a3c921a19717983a5a5744524)]

cookie的值是: NotSet

这样便有了cookie的值

Cookie缺点

  • 不安全,明文(就像上面的示例的结果)
  • 增加带宽消耗
  • 可以被禁用
  • cookie数量有上限(每个浏览器不同)

Sessions

  • 简单的API:将其用作设置签名(以及可选的加密)cookie的简便方法。
  • 内置的后端可将session存储在cookie或文件系统中。
  • Flash消息:一直持续读取的session值。
  • 切换session持久性(又称“记住我”)和设置其他属性的便捷方法。
  • 旋转身份验证和加密密钥的机制。
  • 每个请求有多个session,即使使用不同的后端也是如此。
  • 自定义session后端的接口和基础结构:可以使用通用API检索并批量保存来自不同商店的session。

示例:

package main

import (
    "fmt"
    "net/http"

    "github.com/gorilla/sessions"
)

// 初始化一个cookie存储对象
// something-very-secret应该是一个你自己的密匙,只要不被别人知道就行
var store = sessions.NewCookieStore([]byte("something-very-secret"))

func main() {
    http.HandleFunc("/save", SaveSession)
    http.HandleFunc("/get", GetSession)
    http.HandleFunc("/del", DelSession)
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        fmt.Println("HTTP server failed,err:", err)
        return
    }
}

func SaveSession(w http.ResponseWriter, r *http.Request) {
    // Get a session. We're ignoring the error resulted from decoding an
    // existing session: Get() always returns a session, even if empty.

    // 获取一个session对象,session-name是session的名字
    session, err := store.Get(r, "session-name")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    // 在session中存储值
    session.Values["foo"] = "bar"
    session.Values[42] = 43
    // 保存更改
    session.Save(r, w)
}
func GetSession(w http.ResponseWriter, r *http.Request) {
    session, err := store.Get(r, "session-name")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    foo := session.Values["foo"]
    fmt.Println(foo)
}
func DelSession(w http.ResponseWriter, r *http.Request) {
	session, err := store.Get(r, "session-name")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	// 将session的时间设为小于零的数就是删除
	session.Options.MaxAge = -1
	// 保存更改
	session.Save(r, w)
}

结果:

# 先save后get,可以取到值
bar
# 然后del再get,就取不到值了
<nil>

参考:

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值