跨模块访问数据库失败:JWT鉴权Error 500问题修复方案
Go Fiber JWT鉴权500错误:解决AuthMiddleware空指针异常问题
问题根源
你的AuthMiddleware模块中仅声明了var DB *sql.DB,但未关联main函数里通过app.NewDB()初始化的数据库实例。调用DB.Begin()时,空指针触发panic,直接导致500错误,后续日志也无法正常输出。
修复方案
下面是三种可行的修复方式,优先推荐依赖注入方案,更利于代码解耦和测试:
1. 依赖注入(推荐)
将初始化好的DB实例通过结构体传递给中间件,避免全局变量耦合:
middleware/middleware.go
package middleware import ( "github.com/gofiber/fiber/v2" "database/sql" ) // 用结构体封装中间件,携带DB实例 type AuthMiddleware struct { DB *sql.DB } // 初始化中间件时传入DB func NewAuthMiddleware(db *sql.DB) *AuthMiddleware { return &AuthMiddleware{DB: db} } func (m *AuthMiddleware) Handle(c *fiber.Ctx) error { // 解析JWT获取claims中的email(省略JWT解析逻辑) claims, err := parseJWTFromContext(c) if err != nil { return fiber.ErrUnauthorized } // 使用结构体持有的DB实例操作数据库,避免空指针 tx, err := m.DB.Begin() if err != nil { return fiber.ErrInternalServerError } defer tx.Rollback() // 执行数据库查询逻辑,比如用email查找用户 var userId int err = tx.QueryRow("SELECT id FROM users WHERE email = ?", claims.Email).Scan(&userId) if err != nil { return fiber.ErrUnauthorized } // 验证通过,将用户ID存入上下文,供后续路由使用 c.Locals("userId", userId) return c.Next() }
main.go
func main() { // 初始化数据库连接 db := app.NewDB() if db == nil { panic("failed to initialize database") } // 初始化带DB实例的鉴权中间件 authMiddleware := middleware.NewAuthMiddleware(db) // 启动Fiber应用 app := fiber.New() // 注册中间件 app.Use(authMiddleware.Handle) // 注册需要鉴权的路由 app.Get("/protected", func(c *fiber.Ctx) error { userId := c.Locals("userId").(int) return c.JSON(fiber.Map{"user_id": userId}) }) _ = app.Listen(":3000") }
2. 包级变量赋值
直接将main中初始化的DB实例赋值给middleware包的全局变量(简单但耦合度高,不推荐大型项目使用):
middleware/middleware.go
package middleware import ( "github.com/gofiber/fiber/v2" "database/sql" ) // 声明包级DB变量 var DB *sql.DB func AuthMiddleware(c *fiber.Ctx) error { // 解析JWT获取email claims, err := parseJWTFromContext(c) if err != nil { return fiber.ErrUnauthorized } // 直接使用全局DB变量 tx, err := DB.Begin() if err != nil { return fiber.ErrInternalServerError } defer tx.Rollback() // ...后续数据库操作逻辑 return c.Next() }
main.go
func main() { db := app.NewDB() if db == nil { panic("failed to initialize database") } // 将初始化好的DB赋值给middleware的包级变量 middleware.DB = db app := fiber.New() app.Use(middleware.AuthMiddleware) // ...路由注册 _ = app.Listen(":3000") }
3. 使用全局DI容器
如果项目有全局依赖注入容器(比如wire、dig),可以将DB实例注入到中间件中,这种方式适合大型项目统一管理依赖。
关键注意事项
- 空指针panic会直接导致Fiber返回500错误,务必确保所有操作数据库的实例都已正确初始化
- 优先使用依赖注入方式,降低模块间耦合,方便单元测试时替换Mock数据库
- 数据库操作建议添加错误捕获和日志记录,便于后续排查问题
内容的提问来源于stack exchange,提问作者leo
相关产品推荐
相关产品推荐

