我想做的是,每次创建数据,自动插入当前用户 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 赋值给全局变量,请问这样设置有没有什么问题,或者有什么更好的获取方式?
1
lilei2023 2023-10-11 14:28:26 +08:00
同样是新手,没太理解你这操作
|
2
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 } |
3
Dcynsd OP @cloverzrg2
我之前有在中间件里面设置,但在 Callback 里面拿到的是 nil ```GO DB.WithContext(context.WithValue(context.Background(), constants.ContextKey{}, userModel.ID)) ``` ```GO uid := DB.Statement.Context.Value(constants.ContextKey{}) ``` |
4
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", "[email protected]") c.Next() }) // 在路由器实例中注册处理程序函数 r.POST("/users", createUser) // 启动 Gin 服务器,监听 HTTP 请求 r.Run(":8080") } 我试了一下,这样可以,你对照着看下呢 |
5
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) } } } |
6
Dcynsd OP @jeffmingup 这样确实是可以的,但我不可能每个模型都写一个 BeforeCreate 去设置
|
7
Dcynsd OP @jahanngauss414 调 beforeCreateCallback 这个方法的 c 没有啊,这个回调是在数据库初始化的时候就注册了,我现在是通过定义一个全局变量保存 gin.Context 来实现,就是不知道后面有没有坑
|
8
jahanngauss414 2023-10-11 17:29:32 +08:00
@Dcynsd #7 你这个上下文全局变量肯定不行啊,请求并发全炸了
|
9
jahanngauss414 2023-10-11 17:31:01 +08:00
@Dcynsd #7 创建数据的时候直接把用户 id set 进去就好了吧,这种全局 callback 看起来不是做这种事情的
|
10
rrfeng 2023-10-11 17:31:59 +08:00
不能。直接放到 user 对象里不好么
|
11
stiangao 2023-10-11 17:56:46 +08:00
把 gin.Context 赋值给全局变量 !
WTF??? |
12
Dcynsd OP @jahanngauss414 好吧,主要每张表都有创建人 ID
|
13
Dcynsd OP 查了一下,确实不能放全局,只有重新找其它方式实现
|
14
mshadow 2023-10-11 20:51:40 +08:00 via Android
老老实实一个字段一个字段写,没多大工作量,可维护性高很多。
|
15
langhuishan 2023-10-12 08:57:47 +08:00
你这里逻辑就有问题,数据库初始化怎么可以和用户 id 绑定呢?用户 id 是数据库查询的条件之一,是具体业务时候添加的条件。
|
16
AnroZ 2023-10-12 14:20:48 +08:00
多层调用或多模块间传递变量,又不想老老实实写接口参数,投机的方案就是用全局变量。
当然,为了保证调用上下文的一致性,可以根据逻辑作用域不同,把全局变量分成:全局进程变量、全局线程变量、全局协程变量。 这里,全局协程变量,顾名思义,保证同协程内全局访问到同一个变量,又防止多协程间的访问冲突,当协程结束了,对应的全局协程变量也就回收了。 你可以把 gin.Context 赋值给全局协程变量,前提是得保证用到的地方是同协程内调用,注意下这里的回调会不会切换协程了。 这个投机方案,不知道是否对你有用。 |
17
jeffmingup 2023-10-12 14:48:58 +08:00
|
18
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
|