Gin框架笔记

安装gin框架

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

这一步如果安装不了,需要在goland中设置代理,启用Go模块集成,并把环境设置为:GOPROXY=https://goproxy.io

并且设置GOPATH,勾选为整个GOPATH编制索引和使用系统环境中定义的GOPATH

设置完成后,重启goland

导入gin

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

实例代码:

package main

import "github.com/gin-gonic/gin"
//web图标
import "github.com/thinkerou/favicon"
func main(){
    //创建一个服务
    ginServer := gin.Default()
    //使用图标,图标可以是任何图片
    ginServer.Use(favicon.New("./favicon.ico"))
    //连接数据库的代码

    //访问地址,处理请求   Request Response
    //func(context *gin.Context)  定义匿名函数处理请求,参数“context”是Gin框架中封装了HTTP请求和响应的上下文对象。
    ginServer.GET("/hello",func(context *gin.Context){
           context.JSON(200,gin.H{"msg": "hello.world"})
    })

    //服务器端口
    ginServer.Run(":8080")
}

实例页面:

RESTful API

以前写网站

get /user
post /create_user
post /update_user
post /delete_user

使用RESTful API写网站

get /user
post /user
put /user
delete /user

gin框架支持RESTful API的开发。

实例代码:

package main

import "github.com/gin-gonic/gin"
//web图标
import "github.com/thinkerou/favicon"
func main() {
	//创建一个服务
	ginServer := gin.Default()
	//使用图标,图标可以是任何图片
	ginServer.Use(favicon.New("./favicon.ico"))
	//连接数据库的代码

	//访问地址,处理请求   Request Response
	//func(context *gin.Context)  定义匿名函数处理请求,参数“context”是Gin框架中封装了HTTP请求和响应的上下文对象。
	ginServer.GET("/hello", func(context *gin.Context) {
		context.JSON(200, gin.H{"msg": "hello.world"})
	})
	//Gin RestFul的编写方式
	ginServer.POST("/user", func(c *gin.Context) {
		c.JSON(200, gin.H{"msg": "post,user"})
	})
	ginServer.PUT("/user")
	ginServer.DELETE("/user")
	//服务器端口
	ginServer.Run(":8080")
}

加载静态页面

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)
//web图标
import "github.com/thinkerou/favicon"
func main() {
	//创建一个服务
	ginServer := gin.Default()
	//使用图标,图标可以是任何图片
	ginServer.Use(favicon.New("./favicon.ico"))
        
        // 加载资源文件
        ginServer.Static("/static","./static")
 
	//加载静态页面
	ginServer.LoadHTMLGlob("templates/*")

	//响应一个页面给前端
	ginServer.GET("/user",func(context *gin.Context){
		//context.JSON()
		context.HTML(http.StatusOK,"index.html",gin.H{"msg": "测试"})
	})

	//连接数据库的代码

	//访问地址,处理请求   Request Response
	//func(context *gin.Context)  定义匿名函数处理请求,参数“context”是Gin框架中封装了HTTP请求和响应的上下文对象。
	ginServer.GET("/hello", func(context *gin.Context) {
		context.JSON(200, gin.H{"msg": "hello.world"})
	})
	//Gin RestFul的编写方式
	ginServer.POST("/user", func(c *gin.Context) {
		c.JSON(200, gin.H{"msg": "post,user"})
	})
	ginServer.PUT("/user")
	ginServer.DELETE("/user")
	//服务器端口
	ginServer.Run(":8080")
}

获取前端传入的数据

package main

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

//web图标
import "github.com/thinkerou/favicon"

func main() {
	//创建一个服务
	ginServer := gin.Default()
	//使用图标,图标可以是任何图片
	ginServer.Use(favicon.New("./8.png"))

	// 加载资源文件
	ginServer.Static("/static", "./static")

	//加载静态页面
	ginServer.LoadHTMLGlob("templates/*")

	//响应一个页面给前端
	ginServer.GET("/user", func(context *gin.Context) {
		//context.JSON()
		context.HTML(http.StatusOK, "index.html", gin.H{"msg": "测试"})
	})

	//连接数据库的代码

	//访问地址,处理请求   Request Response
	//func(context *gin.Context)  定义匿名函数处理请求,参数“context”是Gin框架中封装了HTTP请求和响应的上下文对象。
	ginServer.GET("/hello", func(context *gin.Context) {
		context.JSON(200, gin.H{"msg": "hello.world"})
	})
	//Gin RestFul的编写方式
	ginServer.POST("/user", func(c *gin.Context) {
		c.JSON(200, gin.H{"msg": "post,user"})
	})
	ginServer.PUT("/user")
	ginServer.DELETE("/user")

	//获取前端来自的数据
	ginServer.GET("user/info", func(context *gin.Context) {
		userid := context.Query("userid")
		username := context.Query("username")
		context.JSON(http.StatusOK, gin.H{
			"userid":   userid,
			"username": username,
		})
	})
	//另一种方式
	ginServer.GET("user/info/:userid/:username", func(context *gin.Context) {
		userid := context.Param("userid")
		username := context.Param("username")
		context.JSON(http.StatusOK, gin.H{
			"userid":   userid,
			"username": username,
		})
	})
	//POST传参json
	ginServer.POST("/json", func(context *gin.Context) {
		//request.body
		//[]byte
		data, _ := context.GetRawData()

		var m map[string]interface{}
		// 包装为json数据 []byte
		_ = json.Unmarshal(data, &m)
		context.JSON(http.StatusOK, m)

	})
	//POST传参form
	ginServer.POST("/form/login", func(context *gin.Context) {
		username := context.PostForm("username")
		password := context.PostForm("password")
		context.JSON(http.StatusOK, gin.H{
			"msg":      "登录成功",
			"username": username,
			"password": password,
		})
	})

	//服务器端口
	ginServer.Run(":8080")
}

重定向

gitServer.GET("/test123",func(context *gin.Context){
     context.Redirect(http.StatusMovedPermanently,"https://www.nightying.com")
}

错误页面

ginServer.NoRoute(func(context *gin.Context){
   context.HTML(http.StatusNotFound,"404.html",nil)
})

路由组(蓝图)

userGroup := ginServer.Group("/user")
{
      userGroup.GET("/add")
      userGroup.GET("/login")
      userGroup.GET("/logout")
}
order := ginServer.Group("/order")
{
      orderGroup.GET("/add")
      orderGroup.DELETE("/delte")
}

拦截器 – 中间件

(一般用于预处理数据,比如登录,分页,

//自定义中间件 拦截器
func myHandler()(gin.HandlerFunc){
    return func(context *gin.Context){
       //通过自定义的中间件,设置的值,在后续处理只要调用了这个中间件的都可以拿到这里的参数
       context.Set("usersesion","userid-1")
      

       context.Next()  //放行
       context.Abort()  //阻止
  }
}

使用方式:
gitServer.GET("/user/add",myHandler(),func(context *gin.Context){
      //取出中间件的值
      usersesion := context.MustGet("usersesion").(string)
      log.Println(userSession)
}

go语言多线程

package main

import "fmt"
func hhh(){
  fmt.Println("测")
}

func main(){
 go hhh()
 fmt.Println("main")
}

项目实例

(该项目是GDX_helium雏形)

项目结构:

C:.
│  .gitignore    //github上传限制
│  buildsql.go   //新建数据库
│  go.mod        //包导入
│  go.sum
│  main.go       //主函数
│  README.md
│  setsql.go     //测试数据
│  结构.txt
│
├─.idea
│      .gitignore
│      .name
│      GDX_helium.iml
│      modules.xml
│      vcs.xml
│      workspace.xml
│
├─api             //api文档
│  └─v1
│      ├─admin
│      │      admin.go    
│      │
│      ├─client
│      ├─login
│      │      login.go
│      │
│      ├─quest
│      └─template
├─conf       //web相关配置文件
│      config.go
│      config.yaml
│
├─const      //常量
│      enum.go    //枚举文件
│
├─db
│      db.go    //数据库
│
├─log
│      logger.go   //日志
│
├─models      //gorm模型
│      user.go 
│
├─static      //静态文件
├─system      //系统相关
│      init.go //设置
│
├─templates   //模板
└─utils       //复用文件
        file.go

主框架main.go:

package main

import (
	"GDX_helium/api/v1/admin"
	"GDX_helium/conf"
	"GDX_helium/db"
	"GDX_helium/log"
	"GDX_helium/system"
	"github.com/gin-gonic/gin"
)

func main() {
	// 初始化配置
	conf.Init()

	// 初始化日志
	log.Init()

	// 初始化系统
	system.Init()

	// 初始化数据库
	db.Init()

	// 创建Gin引擎
	r := gin.Default()

	// 注册路由
	v1 := r.Group("/api/v1")
	{
		admin.RegisterRoutes(v1)
	}

	// 启动服务器
	r.Run(":8080")
}

数据库文件db.go

package db

import (
	"GDX_helium/conf"
	user "GDX_helium/models"
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"log"
)

var DB *gorm.DB

func Buildsql() { //生成数据库
	err := DB.AutoMigrate(&user.User{}) // 使用 user.User新建user表
	if err != nil {
		log.Fatalf("迁移数据库出错: %v", err)
	}
}

func Init() {
	dbConfig := conf.GetDatabaseConfig() //导入数据库配置信息
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
		dbConfig["user"], dbConfig["password"], dbConfig["host"], dbConfig["port"], dbConfig["name"])

	var err error
	DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) //连接数据库
	if err != nil {
		log.Fatalf("连接数据库错误: %v", err)
	}
}

配置文件config.go

package conf

import (
	"github.com/spf13/viper"
	"log"
)

func Init() {
	// 设置配置文件名 (不包括扩展名)
	viper.SetConfigName("config")
	// 设置配置文件类型
	viper.SetConfigType("yaml")
	// 设置配置文件路径,可以设置多个路径
	viper.AddConfigPath("./conf")
	// 读取配置文件
	err := viper.ReadInConfig()
	if err != nil {
		log.Fatalf("Error reading config file, %s", err)
	}
}

// 获取配置项
func GetServerPort() string {				//端口
	return viper.GetString("server.port")
}

func GetDatabaseConfig() map[string]string {		//获取数据库相关配置
	return map[string]string{
		"type":     viper.GetString("database.type"),
		"host":     viper.GetString("database.host"),
		"port":     viper.GetString("database.port"),
		"user":     viper.GetString("database.user"),
		"password": viper.GetString("database.password"),
		"name":     viper.GetString("database.name"),
	}
}

配置文件config.yaml(主要是参考格式)

# 应用程序配置
app:
  name: GDX_helium
  mode: debug # release

# 服务器端口
server:
  port: 8080

# 数据库
database:
  type: mysql
  host: localhost
  port: 3306
  user: root
  password: 123456
  name: gdx_helium

# 日志
log:
  level: info
  format: text # or json

# JWT配置
jwt:
  secret: key
  expire: 3600 # in seconds

# 缓存
cache:
  type: redis
  host: localhost
  port: 6379
  password: ""

# 邮箱
email:
  smtp_host: smtp.example.com
  smtp_port: 587
  user: [email protected]
  password: email_password

枚举函数enum.go

package enum

import (
	"database/sql/driver"
	"errors"
)

type UserRole string //用户角色

const (
	Admin UserRole = "admin" //管理员
	User  UserRole = "user"  //用户
	Guest UserRole = "block" //冻结
)

func (u *UserRole) Scan(value interface{}) error { //枚举通用
	if value == nil {
		*u = ""
		return nil
	}
	str, ok := value.([]byte)
	if !ok {
		return errors.New("UserRole的数据无效")
	}
	*u = UserRole(str)
	return nil
}

func (u UserRole) Value() (driver.Value, error) { //枚举通用
	return string(u), nil
}

type OrderStatus string

const (
	Pending   OrderStatus = "pending"
	Completed OrderStatus = "completed"
	Canceled  OrderStatus = "canceled"
)

func (o *OrderStatus) Scan(value interface{}) error {
	if value == nil {
		*o = ""
		return nil
	}
	str, ok := value.([]byte)
	if !ok {
		return errors.New("OrderStatus的无效数据")
	}
	*o = OrderStatus(str)
	return nil
}

func (o OrderStatus) Value() (driver.Value, error) {
	return string(o), nil
}

// IsValid 检查状态是否有效
func (s OrderStatus) IsValid() bool {
    switch s {
    case pending, running, ended, block:
        return true
    default:
        return false
    }
}

数据库ORM模型-user.go

package user

import (
	enum "GDX_helium/const"
	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
)

type User struct {
	ID        uint          `gorm:"primaryKey"`                                                     //用户ID
	Name      string        `gorm:"size:100;not null" json:"name"`                                  //用户昵称
	Email     string        `gorm:"size:100;uniqueIndex;null" json:"email"`                         //用户邮箱
	LoginName string        `gorm:"size:100;not null" json:"login_name"`                            //登陆账号
	Password  string        `gorm:"size:100;not null" json:"password"`                              //密码
	Phone     string        `gorm:"size:20;null" json:"phone"`                                      //手机号
	Role      enum.UserRole `gorm:"type:enum('admin', 'user', 'block');default:'user'" json:"role"` //权限
}

// 对比密码
func (u *User) CheckPassword(password string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
	return err == nil
}

// 寻找用户
func GetUserByLoginName(loginName string, db *gorm.DB) (*User, error) {
	var user User
	if err := db.Where("login_name = ?", loginName).First(&user).Error; err != nil {
		return nil, err
	}
	return &user, nil
}

GORM模型关联

type TaskUser struct {
    TaskID uint `gorm:"not null"  json:"task_id"`
    Task   Task `gorm:"foreignKey:TaskID;references:TaskID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"task"`
    UserID uint `gorm:"not null"  json:"user_id"`
    User   User `gorm:"foreignKey:UserID;references:UserID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"user"`
}

//foreignKey:设置外键,references:与对应的表中的字段关联

登录login.go

package admin

import (
	"GDX_helium/db"
	user "GDX_helium/models"
	"github.com/gin-gonic/gin"
	"net/http"
)

type LoginRequest struct {
	LoginName string `json:"login_name" binding:"required"`
	Password  string `json:"password" binding:"required"`
}

func RegisterRoutes(r *gin.RouterGroup) {
	r.GET("/admin", GetActivity)
	r.POST("/login", Login)
}

func GetActivity(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"message": "Activity endpoint",
	})
}

func Login(c *gin.Context) {
	var req LoginRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求"})
		return
	}

	user, err := user.GetUserByLoginName(req.LoginName, db.DB)   //查询
	if err != nil {			//如果err不为nil
		c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的登录名或密码"})
		return
	}
	if !user.CheckPassword(req.Password) {   //如果密码不一致
		c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的登录名或密码"})
		return
	}
	// 生成并返回 JWT 或其他会话管理方案
	c.JSON(http.StatusOK, gin.H{
		"message":   "Login成功",
		"user_id":   user.ID,
		"user_name": user.Name,
		"role":      user.Role.Value,
	})
}

向数据库添加数据

func add_task(c *gin.Context) {
    var req task_add
    if err := c.ShouldBind(&req); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求"})
        return
    }
    userID := c.MustGet("userID").(uint)
    task := models.Task{
        Introduce: req.Introduce,
        CreatorID: userID,
        BuildTime: time.Now().Unix(),
    }

    if err := db.DB.Create(&task).Error; err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "无法创建任务"})
        return
    }

    c.JSON(http.StatusOK, gin.H{"message": "任务创建成功", "task": task})
}

修改数据

var task models.Task
// 查找任务
    if err := db.DB.First(&task, taskID).Error; err != nil {
        if gorm.ErrRecordNotFound == err {
            c.JSON(http.StatusNotFound, gin.H{"error": "任务未找到"})
        } else {
            c.JSON(http.StatusInternalServerError, gin.H{"error": "无法查找任务"})
        }
        return
    }

// 更新任务字段
    task.Introduce = req.Introduce
    task.Frequency = req.Frequency
    task.Status = req.Status

    // 保存更新
    if err := db.DB.Save(&task).Error; err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "无法更新任务"})
        return
    }

查询数据库

// GetTaskList 查询所有任务列表    Find查询所有列表
func AdminGetTaskList(db *gorm.DB) ([]Task, error) {
    var tasks []Task
    if err := db.Find(&tasks).Error; err != nil {
        return nil, err
    }
    return tasks, nil
}

//查询单个数据    Where().First()
// GetUserByLoginName通过登录名检索用户
func GetUserByLoginName(loginName string, db *gorm.DB) (*User, error) {
    var user User
    if err := db.Where("login_name = ?", loginName).First(&user).Error; err != nil {
        return nil, err
    }
    return &user, nil
}

//联表查询     Joins().Where().Find()
//Joins联合查询
//JOIN task_users ON task_users.task_id = tasks.task_id
//联合task_users表,获取条件为task_users表的task_id预tasks表单task_id相等的数据
//Where筛选数据
//"task_users.user_id = ?",userID
//筛选出task_users.user_id等于传入参数的userID的数据
//Find(&tasks)
//将数据存储至tasks中


func UserGetTaskList(db *gorm.DB, userID uint) ([]Task, error) {
    var tasks []Task

    // 通过中间表 task_user 查询指定用户的任务列表
    if err := db.Joins("JOIN task_users ON task_users.task_id = tasks.task_id").
        Where("task_users.user_id = ?", userID).
        Find(&tasks).Error; err != nil {
        return nil, err
    }

    return tasks, nil
}

JWT的使用

先安装jwt的包:

go get github.com/dgrijalva/jwt-go

JWT创建、生成、验证令牌:

package common

import (
	"GDX_helium/conf"
	"errors"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"net/http"
	"strconv"
	"strings"
	"time"
)

type Claims struct { // JWT结构
	UserID             uint   `json:"user_id"` // 用户ID
	Role               string `json:"role"`    // 用户角色
	jwt.StandardClaims        // 时间
}

// 生成JWT令牌
func GenerateJWT(userID uint, role string) (string, error) {
	JWTConfig := conf.GetJWTConfig() // 导入JWT配置文件
	var jwtKey = []byte(JWTConfig["JWTkey"])
	expire, err := strconv.Atoi(JWTConfig["expire"])
	expirationTime := time.Now().Add(time.Duration(expire) * time.Second)
	claims := &Claims{
		UserID: userID,
		Role:   role,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: expirationTime.Unix(),
		},
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenString, err := token.SignedString(jwtKey)
	if err != nil {
		return "", err
	}
	return tokenString, nil
}

// 验证JWT令牌
func ValidateJWT(tokenString string) (*Claims, error) {
	JWTConfig := conf.GetJWTConfig() // 导入JWT配置文件
	var jwtKey = []byte(JWTConfig["JWTkey"])
	claims := &Claims{}
	token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
		return jwtKey, nil
	})

	if err != nil {
		if ve, ok := err.(*jwt.ValidationError); ok {
			if ve.Errors&jwt.ValidationErrorExpired != 0 {
				return nil, errors.New("令牌已过期")
			}
		}
		return nil, err
	}

	if !token.Valid {
		return nil, errors.New("令牌无效")
	}

	return claims, nil
}

JWT拦截器(中间件):

func JWTAuthMiddleware() gin.HandlerFunc {		//JWT中间件
	return func(c *gin.Context) {
		authHeader := c.GetHeader("Authorization")
		if authHeader == "" {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "认证失败"})
			c.Abort()
			return
		}

		parts := strings.SplitN(authHeader, " ", 2)
		if !(len(parts) == 2 && parts[0] == "Bearer") {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "认证失败"})
			c.Abort()
			return
		}

		claims, err := ValidateJWT(parts[1])
		if err != nil {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的令牌"})
			c.Abort()
			return
		}

		c.Set("userID", claims.UserID)
                c.Set("userRole", claims.Role)
		c.Next()
	}

JWT的使用:

修改main.go:

package main

import (
	"GDX_helium/api/v1/admin"
	"GDX_helium/common"
	"GDX_helium/conf"
	"GDX_helium/db"
	"GDX_helium/log"
	"GDX_helium/system"
	"github.com/gin-gonic/gin"
)

func main() {
	// 初始化配置
	conf.Init()

	// 初始化日志
	log.Init()

	// 初始化系统
	system.Init()

	// 初始化数据库
	db.Init() // 新增此行

	// 创建Gin引擎
	r := gin.Default()

	// 注册公共路由
	public := r.Group("/api/v1")
	admin.RegisterPublicRoutes(public)

	// 注册受保护的路由
	protected := r.Group("/api/v1")
	protected.Use(common.JWTAuthMiddleware())
	admin.RegisterProtectedRoutes(protected)

	// 启动服务器
	r.Run(":8080")
}

登录login.go:

func Login(c *gin.Context) {
    var req LoginRequest
    if err := c.ShouldBind(&req); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求"})
        return
    }

    user, err := models.GetUserByLoginName(req.LoginName, db.DB)
    if err != nil {
        c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的登录名或密码"})
        return
    }

    if !user.CheckPassword(req.Password) {
        c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的登录名或密码"})
        return
    }

    // 生成JWT令牌
    token, err := common.GenerateJWT(user.ID, string(user.Role))
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "无法生成令牌"})
        return
    }

    // 设置JWT令牌在Authorization头中
    c.Header("Authorization", "Bearer "+token)

    // 返回JWT令牌
    c.JSON(http.StatusOK, gin.H{
        "message":   "登录成功",
        "user_id":   user.ID,
        "user_name": user.Name,
        "role":      user.Role,
    })
}

刷新token:

jwt.go:

// 检查令牌是否即将过期并生成新的令牌
func RefreshJWT(tokenString string) (string, error) {
	claims, err := ValidateJWT(tokenString)
	if err != nil {
		return "", err
	}

	// 检查令牌是否即将过期,设置一个合理的时间窗口
	if time.Until(time.Unix(claims.ExpiresAt, 0)) > 30*time.Minute {
		return "", errors.New("令牌未接近过期,无需刷新")
	}

	// 生成新的令牌
	return GenerateJWT(claims.UserID, claims.Role)
}

login.go:

func RefreshToken(c *gin.Context) {
	authHeader := c.GetHeader("Authorization")
	if authHeader == "" {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "请求头中缺少Authorization字段"})
		return
	}

	parts := strings.SplitN(authHeader, " ", 2)
	if !(len(parts) == 2 && parts[0] == "Bearer") {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "请求头中的Authorization格式有误"})
		return
	}

	newToken, err := common.RefreshJWT(parts[1])
	if err != nil {
		c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message": "令牌刷新成功",
		"token":   newToken,
	})
}

在路由中使用:

func RegisterPublicRoutes(r *gin.RouterGroup) {
	r.POST("/login", Login)
}

func RegisterProtectedRoutes(r *gin.RouterGroup) {
	r.GET("/test", test)
}

//测试
func test(c *gin.Context) {
	userID := c.MustGet("userID").(uint)   //提取user_id
	userRole := c.MustGet("userRole").(string)  //提取角色

	c.JSON(http.StatusOK, gin.H{
		"message":   "你已成功访问受保护的路由",
		"user_id":   userID,
		"user_role": userRole,
	})
}

缓存的使用

WebSocket

优化