英文:
Go and Gin: Passing around struct for database context?
问题
我刚刚开始尝试使用Go语言,并希望用它重新实现一个使用Node编写的API服务器。
我在尝试使用依赖注入来传递数据库上下文作为gin中间件时遇到了问题。到目前为止,我已经设置如下:
main.go:
package main
import (
"fmt"
"runtime"
"log"
"github.com/gin-gonic/gin"
"votesforschools.com/api/public"
"votesforschools.com/api/models"
)
type DB struct {
models.DataStore
}
func main() {
ConfigRuntime()
ConfigServer()
}
func Database(connectionString string) gin.HandlerFunc {
dbInstance, err := models.NewDB(connectionString)
if err != nil {
log.Panic(err)
}
db := &DB{dbInstance}
return func(c *gin.Context) {
c.Set("DB", db)
c.Next()
}
}
func ConfigRuntime() {
nuCPU := runtime.NumCPU()
runtime.GOMAXPROCS(nuCPU)
fmt.Printf("Running with %d CPUs\n", nuCPU)
}
func ConfigServer() {
gin.SetMode(gin.ReleaseMode)
router := gin.New()
router.Use(Database("<connectionstring>"))
router.GET("/public/current-vote-pack", public.GetCurrentVotePack)
router.Run(":1000")
}
models/db.go
package models
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
type DataStore interface {
GetVotePack(id string) (*VotePack, error)
}
type DB struct {
*sql.DB
}
func NewDB(dataSource string) (*DB, error) {
db, err := sql.Open("mysql", dataSource)
if err != nil {
return nil, err
}
if err = db.Ping(); err != nil {
return nil, err
}
return &DB{db}, nil
}
models/votepack.go
package models
import (
"time"
"database/sql"
)
type VotePack struct {
id string
question string
description string
startDate time.Time
endDate time.Time
thankYou string
curriculum []string
}
func (db *DB) GetVotePack(id string) (*VotePack, error) {
var votePack *VotePack
err := db.QueryRow(
"SELECT id, question, description, start_date AS startDate, end_date AS endDate, thank_you AS thankYou, curriculum WHERE id = ?", id).Scan(
&votePack.id, &votePack.question, &votePack.description, &votePack.startDate, &votePack.endDate, &votePack.thankYou, &votePack.curriculum)
switch {
case err == sql.ErrNoRows:
return nil, err
case err != nil:
return nil, err
default:
return votePack, nil
}
}
所以,根据上述所有内容,我想将models.DataSource作为中间件传递,以便可以像这样访问它:
public/public.go
package public
import (
"github.com/gin-gonic/gin"
)
func GetCurrentVotePack(context *gin.Context) {
db := context.Keys["DB"]
votePack, err := db.GetVotePack("c5039ecd-e774-4c19-a2b9-600c2134784d")
if err != nil{
context.String(404, "Votepack Not Found")
}
context.JSON(200, votePack)
}
然而,我得到了public\public.go:10: db.GetVotePack undefined (type interface {} is interface with no methods)
的错误。
当我在调试器中检查时(使用带插件的Webstorm),db只是一个空对象。我试图避免使用全局变量。
英文:
I've just started trying out Go, and I'm looking to re-implement an API server written in node with it.
I've hit a hurdle with trying to use dependency injection to pass around a database context as a gin middleware. So far I've set it up as this:
main.go:
package main
import (
"fmt"
"runtime"
"log"
"github.com/gin-gonic/gin"
"votesforschools.com/api/public"
"votesforschools.com/api/models"
)
type DB struct {
models.DataStore
}
func main() {
ConfigRuntime()
ConfigServer()
}
func Database(connectionString string) gin.HandlerFunc {
dbInstance, err := models.NewDB(connectionString)
if err != nil {
log.Panic(err)
}
db := &DB{dbInstance}
return func(c *gin.Context) {
c.Set("DB", db)
c.Next()
}
}
func ConfigRuntime() {
nuCPU := runtime.NumCPU()
runtime.GOMAXPROCS(nuCPU)
fmt.Printf("Running with %d CPUs\n", nuCPU)
}
func ConfigServer() {
gin.SetMode(gin.ReleaseMode)
router := gin.New()
router.Use(Database("<connectionstring>"))
router.GET("/public/current-vote-pack", public.GetCurrentVotePack)
router.Run(":1000")
}
models/db.go
package models
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
type DataStore interface {
GetVotePack(id string) (*VotePack, error)
}
type DB struct {
*sql.DB
}
func NewDB(dataSource string) (*DB, error) {
db, err := sql.Open("mysql", dataSource)
if err != nil {
return nil, err
}
if err = db.Ping(); err != nil {
return nil, err
}
return &DB{db}, nil
}
models/votepack.go
package models
import (
"time"
"database/sql"
)
type VotePack struct {
id string
question string
description string
startDate time.Time
endDate time.Time
thankYou string
curriculum []string
}
func (db *DB) GetVotePack(id string) (*VotePack, error) {
var votePack *VotePack
err := db.QueryRow(
"SELECT id, question, description, start_date AS startDate, end_date AS endDate, thank_you AS thankYou, curriculum WHERE id = ?", id).Scan(
&votePack.id, &votePack.question, &votePack.description, &votePack.startDate, &votePack.endDate, &votePack.thankYou, &votePack.curriculum)
switch {
case err == sql.ErrNoRows:
return nil, err
case err != nil:
return nil, err
default:
return votePack, nil
}
}
So with all of the above, I want to pass the models.DataSource around as a middleware so it can be accessed like this:
public/public.go
package public
import (
"github.com/gin-gonic/gin"
)
func GetCurrentVotePack(context *gin.Context) {
db := context.Keys["DB"]
votePack, err := db.GetVotePack("c5039ecd-e774-4c19-a2b9-600c2134784d")
if err != nil{
context.String(404, "Votepack Not Found")
}
context.JSON(200, votePack)
}
However I get public\public.go:10: db.GetVotePack undefined (type interface {} is interface with no methods)
When I inspect in the debugger (using Webstorm with plugin) the db is just an empty object. I'm trying to be good and avoid global variable use
答案1
得分: 34
我不认为context
应该被用作依赖注入容器:https://golang.org/pkg/context/
包context定义了Context类型,它在API边界和进程之间传递截止时间、取消信号和其他请求范围的值。
我更愿意使用:
package public
type PublicController struct {
Database *DB
}
func (c *PublicController) GetCurrentVotePack(context *gin.Context) {
votePack, err := c.Database.GetVotePack("c5039ecd-e774-4c19-a2b9-600c2134784d")
if err != nil{
context.String(404, "Votepack Not Found")
}
context.JSON(200, votePack)
}
并在main函数中配置你的控制器:
func main() {
pCtrl := PublicController { Database: models.NewDB("<connectionstring>") }
router := gin.New()
router.GET("/public/current-vote-pack", pCtrl.GetCurrentVotePack)
router.Run(":1000")
}
英文:
I don't think context
should be used as DI container: https://golang.org/pkg/context/
>Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.
I would rather use:
package public
type PublicController struct {
Database *DB
}
func (c *PublicController) GetCurrentVotePack(context *gin.Context) {
votePack, err := c.Database.GetVotePack("c5039ecd-e774-4c19-a2b9-600c2134784d")
if err != nil{
context.String(404, "Votepack Not Found")
}
context.JSON(200, votePack)
}
and configure your controller in main:
func main() {
pCtrl := PublicController { Database: models.NewDB("<connectionstring>") }
router := gin.New()
router.GET("/public/current-vote-pack", pCtrl.GetCurrentVotePack)
router.Run(":1000")
}
答案2
得分: 13
context.Keys
中的值都是interface{}
类型,因此在将其转换回*DB
类型之前,db
将无法调用*DB
类型的方法。
安全的方式:
db, ok := context.Keys["DB"].(*DB)
if !ok {
// 处理没有*DB实例的情况
}
// db 现在是一个*DB值
不太安全的方式,如果context.Keys["DB"]
不是*DB
类型的值,将会引发 panic:
db := context.Keys["DB"].(*DB)
// db 现在是一个*DB值
Effective Go中有相关的内容。
英文:
The values within context.Keys
are all of type interface{}
, so db
will not be able to call methods from type *DB
until it's converted back to that type.
The safe way:
db, ok := context.Keys["DB"].(*DB)
if !ok {
//Handle case of no *DB instance
}
// db is now a *DB value
The less safe way, which will panic if context.Keys["DB"]
is not a value of type *DB
:
db := context.Keys["DB"].(*DB)
// db is now a *DB value
Effective Go has a section on this.
答案3
得分: 1
你需要使用类型断言将接口(db := context.Keys["DB"])转换为有用的内容。例如,可以参考这篇帖子:https://stackoverflow.com/questions/18041334/convert-interface-to-int-in-go-lang
英文:
You would need type assertion to convert the interface (db := context.Keys["DB"]) into something useful. See for example this post: https://stackoverflow.com/questions/18041334/convert-interface-to-int-in-go-lang
答案4
得分: 0
当在启动过程中将DB设置为上下文时,还有另一种方法来执行它。
db := ctx.MustGet("DB").(*gorm.DB)
如果给定的键存在,则MustGet返回其对应的值,否则会引发错误。
英文:
There's another way to do it when DB is set to the context during startup.
db := ctx.MustGet("DB").(*gorm.DB)
MustGet returns the value for the given key if it exists, otherwise it panics.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论