我在golang上写api,遇到了错误。在一个请求服务器返回错误后,sql 数据库已关闭。我想通过上下文传输数据库连接。
main.go
func main() { app := fiber.new() db, err := sqlx.connect("pgx", os.getenv("postgresql_url")) if err != nil { panic(err) } if err = db.ping(); err != nil { panic(err) } db.setmaxopenconns(10) db.setmaxidleconns(5) db.setconnmaxlifetime(5 * time.minute) db.setconnmaxidletime(5 * time.minute) defer db.close() configure_router.configurerouter(app, db) if err = app.listen(os.getenv("port")); err != nil { log.fatalln(err) } }
configure_router.go
func configurerouter(app *fiber.app, db *sqlx.db) { //middlewares app.use(logger.new(logger.config{ format: "[${ip}]:${port} ${time} ${status} - ${method} ${path}\n", })) app.use(cors.new(cors.config{ //alloworigins: "http://localhost:3000", allowheaders: "origin, content-type, accept", })) app.use("/api", func(ctx *fiber.ctx) error { ctx.context().setuservalue("dbconn", db) return ctx.next() }) //authentication endpoints app.post("api/register", register.register) app.post("api/auth/login", login.login) }
注册.go
func register(ctx *fiber.ctx) error { conn := ctx.context().uservalue("dbconn").(*sqlx.db) var in in if err := ctx.bodyparser(&in); err != nil { return make_response.makeinforesponse(ctx, fiber.statusunprocessableentity, 1, err.error()) } if in.email == "" || in.password == "" { return make_response.makeinforesponse(ctx, fiber.statusbadrequest, 1, "incorrect data input") } elementexist := false err := conn.get(&elementexist, "select exists(select email from users where email = $1)", in.email) // here programm fall in second request if err != nil { return make_response.makeinforesponse(ctx,fiber.statusinternalservererror, 1, err.error()) } if elementexist { return make_response.makeinforesponse(ctx, fiber.statusbadrequest, 1, "user already registered!") } passwordhash, err := hash_passwords.hashpassword(in.password) if err != nil { return err } _, err = conn.exec("insert into users (email, password_hash) values ($1, $2)", in.email, passwordhash) if err != nil { return make_response.makeinforesponse(ctx, fiber.statusinternalservererror, 1, err.error()) } return make_response.makeinforesponse(ctx, fiber.statusok, 0, "registration was successful!") }
如果我在 /api/register 中发送请求并且用户已经在第一个请求中注册,我会得到
要求:
{ "email": "[email protected]", "password": "123123123" }
第一反应:
{ "error_code": 0, "message": "user already registered!" }
但是如果我想发送另一个请求,我会得到:
{ "error_code": 1, "message": "sql: database is closed", }
我想通过上下文传输数据库连接。
不要。这不仅是不好的做法,而且实际上是 fasthttp.requestctx
本身在每次请求后关闭数据库。上下文应仅包含请求特定值。全局数据库连接几乎不是特定于请求的。
请参阅 setuservalue
的文档,特别是最后一个段落:
从顶层requesthandler返回后,所有值都会从ctx中删除。此外,在从 ctx 中删除值之前,会在每个实现 io.closer 的值上调用 close 方法。
一个快速修复方法是在闭包中捕获数据库:
func Register(db *sqlx.DB) (fn func(*fiber.Ctx) error) { return func(ctx *fiber.Ctx) error { // ... elementExist := false err := db.Get(&elementExist, "select exists(select email from users where email = $1)", in.Email) // ... } } // ... // delete this or comment it out // app.Use("/api", func(ctx *fiber.Ctx) error { // ctx.Context().SetUserValue("dbConn", db) // return ctx.Next() // }) app.Post("api/register", register.Register(db))
以上是数据库连接在第一个请求后关闭的详细内容。更多信息请关注PHP中文网其他相关文章!