在Gin框架中从中间件向路由传递CurrentUser结构体的问题
问题根源
你的panic是因为值类型与nil的不兼容:你定义的CurrentUser是结构体值类型,但中间件在未登录时执行了c.Set("currentUser", nil)——此时c.Get拿到的是interface{}类型的nil,而Go里值类型不能持有nil,尝试将这个nil断言为CurrentUser值类型必然触发panic。
下面提供两种可行的解决方案:
方案一:改用指针类型传递CurrentUser
这种方式贴合你原本用nil表示未登录的逻辑,是Go中更常用的写法:
1. 修改中间件代码
把user定义为指针类型,未登录时设置为nil指针:
func LoginMiddleware(database *sqlx.DB) gin.HandlerFunc { return func(c *gin.Context) { var ( err error cookie string user *CurrentUser // 改为指针类型 ) cookie, err = c.Cookie("cookie") if err != nil { c.Set("currentUser", nil) // 未登录时设为nil指针 c.Next() return } // 初始化指针并分配内存 user = &CurrentUser{} err = database.Get(user, "SELECT user_id, username, email FROM users WHERE user_id = (SELECT user_id FROM sessions WHERE cookie = ?)", cookie) if err != nil { c.Set("currentUser", nil) c.Next() return } user.Cookie = cookie c.Set("currentUser", user) c.Next() } }
2. 修改路由处理函数
断言为指针类型,并判断是否为nil来处理未登录情况:
func HandleProfileGet(c *gin.Context) { var ( currentUser *CurrentUser // 改为指针类型 userSessions []SessionRow err error ) getCurrentUser, ok := c.Get("currentUser") if !ok { c.String(401, "Unauthorized") return } currentUser, ok = getCurrentUser.(*CurrentUser) if !ok || currentUser == nil { c.String(401, "Unauthorized") return } err = Database.Select(&userSessions, "SELECT session_id, user_id, cookie FROM sessions WHERE user_id = ?", currentUser.UserId) if err != nil { c.String(500, "Internal server error") return } c.HTML(200, "profile", gin.H{ "CurrentUser": currentUser, "UserSessions": userSessions, }) }
模板使用无需修改,直接通过{{.CurrentUser.Username}}访问即可,Go模板会自动处理指针。
方案二:保留值类型,添加登录状态标识
如果不想用指针,可以给CurrentUser新增一个IsLoggedIn字段,用它判断登录状态:
1. 修改CurrentUser结构体
type CurrentUser struct { UserId int Username string Email string Cookie string IsLoggedIn bool // 新增字段,标识是否登录 }
2. 修改中间件代码
未登录时设置零值结构体并标记IsLoggedIn=false:
func LoginMiddleware(database *sqlx.DB) gin.HandlerFunc { return func(c *gin.Context) { var ( err error cookie string user CurrentUser ) cookie, err = c.Cookie("cookie") if err != nil { // 未登录时设置IsLoggedIn为false c.Set("currentUser", CurrentUser{IsLoggedIn: false}) c.Next() return } err = database.Get(&user, "SELECT user_id, username, email FROM users WHERE user_id = (SELECT user_id FROM sessions WHERE cookie = ?)", cookie) if err != nil { c.Set("currentUser", CurrentUser{IsLoggedIn: false}) c.Next() return } user.Cookie = cookie user.IsLoggedIn = true // 登录成功设为true c.Set("currentUser", user) c.Next() } }
3. 修改路由处理函数
直接断言为值类型,通过IsLoggedIn判断登录状态:
func HandleProfileGet(c *gin.Context) { var ( currentUser CurrentUser userSessions []SessionRow err error ) getCurrentUser, ok := c.Get("currentUser") if !ok { c.String(401, "Unauthorized") return } currentUser, okType := getCurrentUser.(CurrentUser) if !okType || !currentUser.IsLoggedIn { c.String(401, "Unauthorized") return } err = Database.Select(&userSessions, "SELECT session_id, user_id, cookie FROM sessions WHERE user_id = ?", currentUser.UserId) if err != nil { c.String(500, "Internal server error") return } c.HTML(200, "profile", gin.H{ "CurrentUser": currentUser, "UserSessions": userSessions, }) }
模板中还可以用{{if .CurrentUser.IsLoggedIn}}来快速区分登录状态,渲染不同内容。
内容的提问来源于stack exchange,提问作者Fluffy Bean
相关产品推荐
相关产品推荐

