从 go-chi 路由处理程序访问数据库实例
Access DB instance from go-chi route handlers
我正在尝试使用 go-chi 和 Gorm 构建 REST API。
我不确定应该如何将 Gorm 数据库实例传递给路由处理程序。
或者如果我应该为每个处理程序创建一个实例,这对我来说不合适。
我应该使用中间件、依赖注入还是其他?这里推荐的模式是什么?
package main
import (
"encoding/json"
"fmt"
"github.com/go-chi/chi/v5"
"log"
"net/http"
"os"
"time"
)
func main() {
r := chi.NewRouter()
r.Get("/", indexHandler)
port := os.Getenv("PORT")
if port == "" {
port = "8080"
log.Printf("Defaulting to port %s", port)
}
db := Connect()
migrations(db)
logStartServer(port)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), r))
}
func logStartServer(port string) {
log.Printf("Listening on port %s", port)
log.Printf("Open http://localhost:%s in the browser", port)
}
func indexHandler(w http.ResponseWriter, r *http.Request) {
//How can I access db here?
//result := db.Find(&users)
policy := InsurancePolicy{ValidFrom: time.Now()}
err := json.NewEncoder(w).Encode(policy)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
}
}
使用方法而不是函数。这允许您使用这些方法的接收者传递处理程序所需的任何信息:
type MyHandler struct {
DB *gorm.DB
}
func (m MyHandler) IndexHandler(w http.ResponseWriter, r *http.Request) {
// Use m.DB here
}
主要内容:
handler:=mypkg.MyHandler{DB:gormDB}
r.Get("/", handler.IndexHandler)
在某些情况下,闭包更有意义。
func GetIndexHandler(db *gorm.DB) func(http.ResponseWriter,*http.Request) {
return func(w http.ResponseWriter,req *http.Request) {
// Implement index handler here, using db
}
}
func main() {
...
r.Get("/", GetIndexHandler(db))
如果您的项目较小,将数据库实例声明为全局变量非常方便。
许多组织数据库访问的方法都被很好地记录在案here。选择适合您的需求。
在 DB/query 函数本身中。我个人为控制器制作了一个单独的包,为服务制作了一个单独的包。我在控制器(具有我的处理程序函数)中处理所有请求验证和 HTTP 内容。然后,如果一切正常,我会调用一个服务包。服务包是调用数据库以及任何其他服务或 API 集成的服务包。
然而,无论您在哪里调用 DB,通常您都在调用一个 db
包,该包具有一堆查询函数,这些函数的名称很友好,例如 db.GetAccountByID
或类似的名称。好吧,db
函数正是您传递 *sql.DB
或 *gorm.DB
对象的地方。
一个例子是...
package db
func GetAccountByID(id int, db *gorm.DB) (*model.Account, error) {
if db == nil {
db = conn // conn is the package level db connection object
}
//...
}
通常,当服务器启动时,我会创建数据库连接(用作连接池),因此没有必要将它传递给函数。那么,为什么要这样做?好吧,这是因为测试。您不希望您的数据库处理程序接触到包级别的数据库连接对象,因为对该功能进行隔离测试变得更加困难。
因此,如果为 DB 值传入 nil
,则此函数签名为您提供了可测试性和初始 if
条件仍然使用单个中央 DB 连接对象,该值始终是 nil
除非你正在测试。
这只是一种方法,但我已经成功使用了多年。
我正在尝试使用 go-chi 和 Gorm 构建 REST API。
我不确定应该如何将 Gorm 数据库实例传递给路由处理程序。
或者如果我应该为每个处理程序创建一个实例,这对我来说不合适。
我应该使用中间件、依赖注入还是其他?这里推荐的模式是什么?
package main
import (
"encoding/json"
"fmt"
"github.com/go-chi/chi/v5"
"log"
"net/http"
"os"
"time"
)
func main() {
r := chi.NewRouter()
r.Get("/", indexHandler)
port := os.Getenv("PORT")
if port == "" {
port = "8080"
log.Printf("Defaulting to port %s", port)
}
db := Connect()
migrations(db)
logStartServer(port)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), r))
}
func logStartServer(port string) {
log.Printf("Listening on port %s", port)
log.Printf("Open http://localhost:%s in the browser", port)
}
func indexHandler(w http.ResponseWriter, r *http.Request) {
//How can I access db here?
//result := db.Find(&users)
policy := InsurancePolicy{ValidFrom: time.Now()}
err := json.NewEncoder(w).Encode(policy)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
}
}
使用方法而不是函数。这允许您使用这些方法的接收者传递处理程序所需的任何信息:
type MyHandler struct {
DB *gorm.DB
}
func (m MyHandler) IndexHandler(w http.ResponseWriter, r *http.Request) {
// Use m.DB here
}
主要内容:
handler:=mypkg.MyHandler{DB:gormDB}
r.Get("/", handler.IndexHandler)
在某些情况下,闭包更有意义。
func GetIndexHandler(db *gorm.DB) func(http.ResponseWriter,*http.Request) {
return func(w http.ResponseWriter,req *http.Request) {
// Implement index handler here, using db
}
}
func main() {
...
r.Get("/", GetIndexHandler(db))
如果您的项目较小,将数据库实例声明为全局变量非常方便。
许多组织数据库访问的方法都被很好地记录在案here。选择适合您的需求。
在 DB/query 函数本身中。我个人为控制器制作了一个单独的包,为服务制作了一个单独的包。我在控制器(具有我的处理程序函数)中处理所有请求验证和 HTTP 内容。然后,如果一切正常,我会调用一个服务包。服务包是调用数据库以及任何其他服务或 API 集成的服务包。
然而,无论您在哪里调用 DB,通常您都在调用一个 db
包,该包具有一堆查询函数,这些函数的名称很友好,例如 db.GetAccountByID
或类似的名称。好吧,db
函数正是您传递 *sql.DB
或 *gorm.DB
对象的地方。
一个例子是...
package db
func GetAccountByID(id int, db *gorm.DB) (*model.Account, error) {
if db == nil {
db = conn // conn is the package level db connection object
}
//...
}
通常,当服务器启动时,我会创建数据库连接(用作连接池),因此没有必要将它传递给函数。那么,为什么要这样做?好吧,这是因为测试。您不希望您的数据库处理程序接触到包级别的数据库连接对象,因为对该功能进行隔离测试变得更加困难。
因此,如果为 DB 值传入 nil
,则此函数签名为您提供了可测试性和初始 if
条件仍然使用单个中央 DB 连接对象,该值始终是 nil
除非你正在测试。
这只是一种方法,但我已经成功使用了多年。