如何使用Gin Web框架将参数传递给Golang中的路由器处理程序?

I'm using Gin, https://gin-gonic.github.io/gin/, to build a simple RESTful JSON API with Golang.

The routes are setup with something like this:

func testRouteHandler(c *gin.Context) {
    // do smth
}

func main() {
    router := gin.Default()
    router.GET("/test", testRouteHandler)
    router.Run(":8080")
}

My question is how can I pass down an argument to the testRouteHandler function? For example a common database connection could be something that one wants to reuse among routes.

Is the best way to have this in a global variable? Or is there some way in Go to pass along an extra variable to the testRouteHandler function? Are there optional arguments for functions in Go?

PS. I'm just getting started in learning Go, so could be something obvious that I'm missing :)

Using the link i posted on comments, I have created a simple example.

package main

import (
    "log"

    "github.com/gin-gonic/gin"
    "github.com/jinzhu/gorm"
    _ "github.com/mattn/go-sqlite3"
)

// ApiMiddleware will add the db connection to the context
func ApiMiddleware(db gorm.DB) gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Set("databaseConn", db)
        c.Next()
    }
}

func main() {
    r := gin.New()

    // In this example, I'll open the db connection here...
    // In your code you would probably do it somewhere else
    db, err := gorm.Open("sqlite3", "./example.db")
    if err != nil {
        log.Fatal(err)
    }

    r.Use(ApiMiddleware(db))

    r.GET("/api", func(c *gin.Context) {
        // Don't forget type assertion when getting the connection from context.
        dbConn, ok := c.MustGet("databaseConn").(gorm.DB)
        if !ok {
            // handle error here...
        }

        // do your thing here...
    })

    r.Run(":8080")
}

This is just a simple POC. But i believe it's a start. Hope it helps.

I would avoid stuffing 'application scoped' dependencies (e.g. a DB connection pool) into a request context. Your two 'easiest' options are:

  1. Make it a global. This is OK for smaller projects, and *sql.DB is thread-safe.
  2. Pass it explicitly in a closure so that the return type satisfies gin.HandlerFunc

e.g.

// SomeHandler returns a `func(*gin.Context)` to satisfy Gin's router methods
// db could turn into an 'Env' struct that encapsulates all of your
// app dependencies - e.g. DB, logger, env vars, etc.
func SomeHandler(db *sql.DB) gin.HandlerFunc {
    fn := func(c *gin.Context) {
        // Your handler code goes in here - e.g.
        rows, err := db.Query(...)

        c.String(200, results)
    }

    return gin.HandlerFunc(fn)
}

func main() {
    db, err := sql.Open(...)
    // handle the error

    router := gin.Default()
    router.GET("/test", SomeHandler(db))
    router.Run(":8080")
}

Alright, I have given you a simple example. It should work. You can extend it as per your need

func main() {
    router := gin.Default()
    router.GET("/test/:id/:name", testRouteHandler)
    router.Run(":8080")
}

func testRouteHandler(c *gin.Context) {
    id := c.Params.ByName("id")
    name := c.Params.ByName("name")
}

Now you will have to call your handler as below http://localhost:8080/test/1/myname

Late to the party, so far here is my proposal. Incapsulate methods into the object with private/public vars in it:

package main

import (
    "log"

    "github.com/gin-gonic/gin"
    "github.com/jinzhu/gorm"
    _ "github.com/mattn/go-sqlite3"
)

type HandlerA struct {
    Db gorm.DB
}

func (this *HandlerA) Get(c *gin.Context) {

    log.Info("[%#f]", this.Db)
    // do your thing here...
}

func main() {
    r := gin.New()

    // Init, should be separate, but it's ok for this sample:
    db, err := gorm.Open("sqlite3", "./example.db")
    if err != nil {
        log.Fatal(err)
    }

    Obj := new(HandlerA)
    Obj.Db = db // Or init inside Object

    r := gin.New()

    Group := r.Group("api/v1/")
    {
        Group.GET("/storage", Obj.Get)
    }

    r.Run(":8080")
}