2016-02-27 13 views
5

Ho appena iniziato a provare Go e sto cercando di implementare di nuovo un server API scritto nel nodo con esso.Go and Gin: aggirare la struttura per il contesto del database?

Ho raggiunto un ostacolo con il tentativo di utilizzare l'iniezione delle dipendenze per aggirare un contesto di database come un middleware di gin. Finora ho impostato in su come questo:

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") 
} 

modelli/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 
} 

modelli/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 
     } 
} 

Quindi, con tutto quanto sopra, voglio passare i modelli.DataSource in giro come un middleware in modo che sia possibile accedervi in ​​questo modo:

pubblico/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) 
} 

Tuttavia ottengo public\public.go:10: db.GetVotePack undefined (type interface {} is interface with no methods)

Quando ho ispezionare nel debugger (usando WebStorm con plugin) il db è solo un oggetto vuoto. Sto cercando di essere buono ed evito l'uso di variabili globali

risposta

6

I valori all'interno context.Keys sono tutti di tipo interface{}, quindi db non sarà in grado di chiamare i metodi di tipo *DB fino a quando è riconvertito a quel tipo.

Il modo sicuro:

db, ok := context.Keys["DB"].(*DB) 
if !ok { 
     //Handle case of no *DB instance 
} 
// db is now a *DB value 

Il modo meno sicuro, che panico se context.Keys["DB"] non è un valore di tipo *DB:

db := context.Keys["DB"].(*DB) 
// db is now a *DB value 

Effective Go ha una sezione su questo.

+0

Perfetto, sembrava risolverlo. –

+0

Mentre questo risolve il problema, mi sembra un pessimo design. Affidarsi a qualcuno che mette le strutture dell'interfaccia {} in un contesto generico non sembra un vero e proprio progetto fortemente tipizzato. Immagino che la maggior parte dell'errore sia dovuto al fatto che gin si aspetta una funzione per il metodo handle invece di un'interfaccia –

+0

@SnoProblem È un buon modo per passare la connessione al database tramite Context? C'è qualche altro modo e? –