Как передать аргументы обработчикам маршрутизаторов в Голанге, используя инфраструктуру Gin?

Я использую Gin, https://gin-gonic.github.io/gin/, для создания простого RESTful JSON API с Golang.

Маршруты настроены примерно так:

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

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

Мой вопрос в том, как передать аргумент функции testRouteHandler? Например, общее соединение с базой данных может быть тем, что нужно повторно использовать среди маршрутов.

Это лучший способ получить это в глобальной переменной? Или есть какой-то путь в Go, чтобы передать дополнительную переменную функции testRouteHandler? Существуют ли дополнительные аргументы для функций в Go?

PS. Я только начинаю изучать Go, так что может быть что-то очевидное, что мне не хватает :)

Ответ 1

Используя ссылку, опубликованную в комментариях, я создал простой пример.

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

Это просто POC. Но я считаю, что это начало. Надеюсь, поможет.

Ответ 2

Я бы избегал наложения "зависимых от приложения" зависимостей (например, пула соединений БД) в контексте запроса. Ваши два "самых простых" варианта:

  1. Сделайте это глобальным. Это нормально для небольших проектов, и *sql.DB является потокобезопасным.
  2. Пропустите его явно в закрытии, так что возвращаемый тип удовлетворяет gin.HandlerFunc

например

// SomeHandler returns a 'func(*gin.Context)' to satisfy Gin 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")
}

Ответ 3

Поздно к партии, пока это мое предложение. Вложить методы в объект с частными/открытыми vars в нем:

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

Ответ 4

Хорошо, я привел вам простой пример. Он должен работать. Вы можете расширить его в соответствии с вашими потребностями

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

Теперь вам нужно будет вызвать своего обработчика ниже : http://localhost: 8080/test/1/myname