Как передать аргументы обработчикам маршрутизатора в Golang с помощью веб-фреймворка 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, так что может быть что-то очевидное, что мне не хватает :)


person Niklas9    schedule 02.12.2015    source источник
comment
Я думаю, вы ищете промежуточное ПО HTTP. Это хорошее место для начала nicolasmerouze .com / middlewares-golang-best-practice-examples.   -  person MIkCode    schedule 02.12.2015
comment
Как сказал @MIkCode, промежуточное ПО - это путь ... взгляните на пользовательское промежуточное ПО Gin. github.com/gin-gonic/gin#custom-middleware   -  person rcmgleite    schedule 02.12.2015


Ответы (6)


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

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. Но я считаю, что это начало. Надеюсь, это поможет.

person rcmgleite    schedule 02.12.2015
comment
Как сделать то же самое, добавить соединение db в контекст в chi framework? - person Sougata; 26.03.2020
comment
К сожалению, это лучший ответ. Это не идеально, потому что Set и Get создают зависимость, в которой шаблон закрытия может просто передавать параметры из промежуточного программного обеспечения во вложенный маршрут. Однако Gin, естественно, не использует шаблон закрытия, поэтому некоторые методы, такие как Use и Group, становятся сложными. - person tim-montague; 30.10.2020

Я бы не стал вставлять зависимости, ограниченные областью применения (например, пул соединений с БД), в контекст запроса. Два ваших «самых простых» варианта:

  1. Сделайте его глобальным. Это нормально для небольших проектов, и *sql.DB является потокобезопасным.
  2. Передайте его явно в замыкании, чтобы возвращаемый тип удовлетворял 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")
}
person elithrar    schedule 03.12.2015
comment
Это должен быть рекомендуемый ответ. - person Indradhanush Gupta; 29.01.2017
comment
К сожалению, у меня возникло несколько проблем с тем, чтобы заставить этот шаблон закрытия хорошо работать с фреймворком Gin при использовании таких методов, как Use и Group (github.com/gin-gonic/gin#grouping-routes), и я бы рекомендовал использовать методы Set и Get, пока Gin не будет лучше разработан. Я редко выступаю за худший дизайн, но лучше быть последовательным в подходе и использовать фреймворк. Отличный образец, но Джина еще нет. - person tim-montague; 30.10.2020

Поздно к вечеринке, пока что вот мое предложение. Инкапсулируйте методы в объект с частными / общедоступными варами:

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")
}
person wildneuro    schedule 27.04.2017

Мне нравится пример wildneuro, но я бы сделал один лайнер для настройки обработчика

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)
    }
 
    r := gin.New()

    Group := r.Group("api/v1/")
    {
        Group.GET("/storage", (&HandlerA{Db: db}).Get)
    }

    r.Run(":8080")
}
person Joakim Karlsson    schedule 28.04.2021

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

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

person deepak    schedule 27.02.2016

Попробую подробно объяснить, чтобы вы не запутались.

  1. В зависимости от входящего маршрута вы хотите вызвать функцию контроллера. Допустим, ваш входящий маршрут /books, а ваш контроллер BooksController
  2. Ваш BooksController попытается получить книги из базы данных и вернет ответ.

Теперь вам нужен обработчик в вашем BooksController, чтобы вы могли получить доступ к базе данных.

Я бы сделал что-то подобное. Предположим, вы используете DynamoDB, а aws sdk предоставляет *dynamodb.DynamoDB. В зависимости от вашей базы данных измените эту переменную.

  1. Создайте структуру, как показано ниже.
type serviceConnection struct {
    db *dynamoDB.DynamoDB
    // You can have all services declared here 
    // which you want to use it in your controller
}
  1. В вашей основной функции получите информацию о подключении к базе данных. Допустим, у вас уже есть функция initDatabaseConnection, которая возвращает обработчик в db, как показано ниже.

db := initDatabaseConnection() - ›возвращает *dynamodb.DynamoDB

  1. Установите db в структурную переменную.
conn := new(serviceConnection)
conn.db = db
  1. Вызовите метод запроса джин с обработчиком приемника, как показано ниже.
r := gin.Default()
r.GET("/books", conn.BooksController)

Как видите, обработчик джина - это метод контроллера, в качестве получателя которого используется экземпляр вашей структуры.

  1. Теперь создайте метод контроллера с serviceConnection struct Receiver.
func (conn *serviceConnection) BooksController(c *gin.Context) {
    books := getBooks(conn.db)
}

Как вы видите здесь, у вас есть доступ ко всем serviceConnection переменным структуры, и вы можете использовать их в своем контроллере.

person Vasuki Dileep    schedule 10.05.2021