Go和Gin:在数据库上下文中传递结构体?

huangapple go评论87阅读模式
英文:

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 (
        &quot;fmt&quot;
        &quot;runtime&quot;
        &quot;log&quot;
        &quot;github.com/gin-gonic/gin&quot;
        &quot;votesforschools.com/api/public&quot;
        &quot;votesforschools.com/api/models&quot;
)

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 := &amp;DB{dbInstance}

        return func(c *gin.Context) {
                c.Set(&quot;DB&quot;, db)
                c.Next()
        }
}


func ConfigRuntime() {
        nuCPU := runtime.NumCPU()
        runtime.GOMAXPROCS(nuCPU)
        fmt.Printf(&quot;Running with %d CPUs\n&quot;, nuCPU)
}

func ConfigServer() {

        gin.SetMode(gin.ReleaseMode)

        router := gin.New()
        router.Use(Database(&quot;&lt;connectionstring&gt;&quot;))
        router.GET(&quot;/public/current-vote-pack&quot;, public.GetCurrentVotePack)
        router.Run(&quot;:1000&quot;)
}

models/db.go

package models

import (
        &quot;database/sql&quot;
        _ &quot;github.com/go-sql-driver/mysql&quot;
)

type DataStore interface {
        GetVotePack(id string) (*VotePack, error)
}

type DB struct {
        *sql.DB
}

func NewDB(dataSource string) (*DB, error) {
        db, err := sql.Open(&quot;mysql&quot;, dataSource)
        if err != nil {
                return nil, err
        }
        if err = db.Ping(); err != nil {
                return nil, err
        }
        return &amp;DB{db}, nil
}

models/votepack.go

package models

import (
        &quot;time&quot;
        &quot;database/sql&quot;
)

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(
                &quot;SELECT id, question, description, start_date AS startDate, end_date AS endDate, thank_you AS thankYou, curriculum WHERE id = ?&quot;, id).Scan(
                &amp;votePack.id, &amp;votePack.question, &amp;votePack.description, &amp;votePack.startDate, &amp;votePack.endDate, &amp;votePack.thankYou, &amp;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 (
        &quot;github.com/gin-gonic/gin&quot;
)

func GetCurrentVotePack(context *gin.Context) {
        db := context.Keys[&quot;DB&quot;]

        votePack, err := db.GetVotePack(&quot;c5039ecd-e774-4c19-a2b9-600c2134784d&quot;)
        if err != nil{
                context.String(404, &quot;Votepack Not Found&quot;)
        }
        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(&quot;c5039ecd-e774-4c19-a2b9-600c2134784d&quot;)
        if err != nil{
                context.String(404, &quot;Votepack Not Found&quot;)
        }
        context.JSON(200, votePack)
}

and configure your controller in main:

func main() {
        pCtrl := PublicController { Database: models.NewDB(&quot;&lt;connectionstring&gt;&quot;) }

        router := gin.New()
        router.GET(&quot;/public/current-vote-pack&quot;, pCtrl.GetCurrentVotePack)
        router.Run(&quot;:1000&quot;)
}

答案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[&quot;DB&quot;].(*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[&quot;DB&quot;] is not a value of type *DB:

db := context.Keys[&quot;DB&quot;].(*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(&quot;DB&quot;).(*gorm.DB)

MustGet returns the value for the given key if it exists, otherwise it panics.

huangapple
  • 本文由 发表于 2016年2月28日 01:17:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/35672842.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定