求助,新手使用 Golang ,在 Gorm 的 Callback 里面怎样获取 gin.Context?

2023-10-11 14:19:17 +08:00
 Dcynsd

我想做的是,每次创建数据,自动插入当前用户 ID 。

我在中间件里面设置了当前登录用户 ID:

c.Set("uid", 1)

在初始化 Gorm 连接的时候,注册了 Callback 回调:

DB.Callback().Create().Before("gorm:before_create").Register("beforeCreateCallback", beforeCreateCallback)
func beforeCreateCallback(db *gorm.DB) {
	userID := 获取 gin.Context 里的内容
	if _, ok := db.Statement.Schema.FieldsByName["user_id"]; ok {
	    db.Statement.SetColumn("user_id", userID)
	}
}

我想的是定义一个全局变量,在中间件设置完 uid 后,把 gin.Context 赋值给全局变量,请问这样设置有没有什么问题,或者有什么更好的获取方式?

1856 次点击
所在节点    Go 编程语言
18 条回复
lilei2023
2023-10-11 14:28:26 +08:00
同样是新手,没太理解你这操作
cloverzrg2
2023-10-11 14:28:39 +08:00
https://gorm.io/docs/context.html#Context-in-Hooks-x2F-Callbacks

func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
ctx := tx.Statement.Context
// ...
return
}
Dcynsd
2023-10-11 14:40:34 +08:00
@cloverzrg2
我之前有在中间件里面设置,但在 Callback 里面拿到的是 nil

```GO
DB.WithContext(context.WithValue(context.Background(), constants.ContextKey{}, userModel.ID))
```

```GO
uid := DB.Statement.Context.Value(constants.ContextKey{})
```
jeffmingup
2023-10-11 16:52:11 +08:00
package main

import (
"log"

"github.com/gin-gonic/gin"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)

type User struct {
gorm.Model
Name string
Email string
}

func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
if u.Email == "" {
ctx := tx.Statement.Context
if v, ok := ctx.Value("Email").(string); ok {
u.Email = v
}
}
return
}

func main() {
// 初始化 Gin 路由器实例
r := gin.Default()

// 初始化 Gorm 数据库连接
dsn := "root:root@tcp(127.0.0.1:3306)/gorm-example?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}

// 自动迁移模型结构体到数据库表
db.AutoMigrate(&User{})

// 创建处理程序函数
createUser := func(c *gin.Context) {
var user User
if err := c.ShouldBindJSON(&user); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
log.Println("Email:", c.GetString("Email"))
db.WithContext(c).Create(&user)
c.JSON(200, user)
}

// 自定义中间件,设置 example 变量为 123
r.Use(func(c *gin.Context) {
c.Set("Email", "123@qq.com")
c.Next()
})
// 在路由器实例中注册处理程序函数

r.POST("/users", createUser)

// 启动 Gin 服务器,监听 HTTP 请求
r.Run(":8080")
}
我试了一下,这样可以,你对照着看下呢
jahanngauss414
2023-10-11 17:16:22 +08:00
DB.Callback().Create().Before("gorm:before_create").Register("beforeCreateCallback", beforeCreateCallback(c))

func beforeCreateCallback(c *gin.Context) func(db *gorm.DB) {
return func(db *gorm.DB) {
userID := c.Get("xxx")
if _, ok := db.Statement.Schema.FieldsByName["user_id"]; ok {
db.Statement.SetColumn("user_id", userID)
}
}
}
Dcynsd
2023-10-11 17:24:41 +08:00
@jeffmingup 这样确实是可以的,但我不可能每个模型都写一个 BeforeCreate 去设置
Dcynsd
2023-10-11 17:27:04 +08:00
@jahanngauss414 调 beforeCreateCallback 这个方法的 c 没有啊,这个回调是在数据库初始化的时候就注册了,我现在是通过定义一个全局变量保存 gin.Context 来实现,就是不知道后面有没有坑
jahanngauss414
2023-10-11 17:29:32 +08:00
@Dcynsd #7 你这个上下文全局变量肯定不行啊,请求并发全炸了
jahanngauss414
2023-10-11 17:31:01 +08:00
@Dcynsd #7 创建数据的时候直接把用户 id set 进去就好了吧,这种全局 callback 看起来不是做这种事情的
rrfeng
2023-10-11 17:31:59 +08:00
不能。直接放到 user 对象里不好么
stiangao
2023-10-11 17:56:46 +08:00
把 gin.Context 赋值给全局变量 !
WTF???
Dcynsd
2023-10-11 18:06:55 +08:00
@jahanngauss414 好吧,主要每张表都有创建人 ID
Dcynsd
2023-10-11 18:35:59 +08:00
查了一下,确实不能放全局,只有重新找其它方式实现
mshadow
2023-10-11 20:51:40 +08:00
老老实实一个字段一个字段写,没多大工作量,可维护性高很多。
langhuishan
2023-10-12 08:57:47 +08:00
你这里逻辑就有问题,数据库初始化怎么可以和用户 id 绑定呢?用户 id 是数据库查询的条件之一,是具体业务时候添加的条件。
AnroZ
2023-10-12 14:20:48 +08:00
多层调用或多模块间传递变量,又不想老老实实写接口参数,投机的方案就是用全局变量。

当然,为了保证调用上下文的一致性,可以根据逻辑作用域不同,把全局变量分成:全局进程变量、全局线程变量、全局协程变量。

这里,全局协程变量,顾名思义,保证同协程内全局访问到同一个变量,又防止多协程间的访问冲突,当协程结束了,对应的全局协程变量也就回收了。

你可以把 gin.Context 赋值给全局协程变量,前提是得保证用到的地方是同协程内调用,注意下这里的回调会不会切换协程了。

这个投机方案,不知道是否对你有用。
jeffmingup
2023-10-12 14:48:58 +08:00
jeffmingup
2023-10-12 14:55:13 +08:00
要不设置值用 gin 的 c.Set("Email", "123") 取值可以直接用 db.Statement.Context.("Email").(string) 因为 gin 的 context 是自己的实现的,gin 的 ContextWithFallback 参数默认 false ,会直接返回 nil

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/980978

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX