You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

跨模块访问数据库失败: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 13:17:32