Передать ссылку на экземпляр Redis обработчику Gorilla/Mux

Я пытаюсь испачкать руки, играя с Gorilla/Mux и Go-Redis, но здесь я сталкиваюсь с небольшой проблемой реализации.

По сути, у меня есть проект, структурированный следующим образом:

введите описание изображения здесь

Где redismanager.go обрабатывает инициализацию клиента Redis:

package redismanager

import (
    "fmt"
    "github.com/go-redis/redis"
)

func InitRedisClient() redis.Client {
    client := redis.NewClient(&redis.Options{
        Addr    : "localhost:6379",
        Password: "",
        DB      : 0, //default
    })

    pong, err := client.Ping().Result()
    if( err != nil ){
        fmt.Println("Cannot Initialize Redis Client ", err)
    }
    fmt.Println("Redis Client Successfully Initialized . . .", pong)

    return *client
}

Где звонит main.go redismanager.InitRedisClient и инициализирует mux.Handlers:

package main

import (
    "github.com/gorilla/mux"
    "github.com/go-redis/redis"
    "net/http"
    "fmt"
    "log"
    "encoding/json"
    "io/ioutil"
    "../redismanager"
    "../api"
)

type RedisInstance struct {
     RInstance *redis.Client
}

func main() {

    //Initialize Redis Client
    client := redismanager.InitRedisClient()
    //Get current redis instance to get passed to different Gorilla-Mux Handlers
    redisHandler := &RedisInstance{RInstance:&client}

    //Initialize Router Handlers
    r := mux.NewRouter()
    r.HandleFunc("/todo", redisHandler.AddTodoHandler).
                                       Methods("POST")

    fmt.Println("Listening on port :8000 . . .")

    // Bind to a port and pass our router in
    log.Fatal(http.ListenAndServe(":8000", r))

}

Теперь я могу легко определить и дать работать правильно AddTodoHandler в том же файле, как:

func (c *RedisInstance) AddTodoHandler(w http.ResponseWriter, r *http.Request) {
  . . . doSomething
}

Но, чтобы сделать вещи немного более модульными, я пытаюсь переместить все эти RouteHandlers внутри их соответствующих файлов в api пакет. Чтобы сделать это, мне нужно передать ссылку на redisHandler но у меня возникают некоторые трудности при попытке сделать это с помощью обработчика внутри api пакет.

Например, если в основном я добавляю:

r.HandleFunc("/todo/{id}", api.GetTodoHandler(&client)).
                                        Methods("GET") 

с gettodo.go

package api

import (
    "net/http"
    "github.com/gorilla/mux"
    "fmt"
    "encoding/json"
    "github.com/go-redis/redis"
)

func GetTodoHandler(c *RedisInstance) func (w http.ResponseWriter, r *http.Request) {
    func (w http.ResponseWriter, r *http.Request) {
        . . . doSomething
    }
}

Работает хорошо.

Я все еще довольно новичок в Go и не нашел более чистого решения, даже после нескольких исследований и прочтений.

Мой подход правильный или есть лучшие?

3 ответа

Решение

Напишите функцию, которая преобразует функцию с аргументом экземпляра Redis в обработчик HTTP:

func redisHandler(c *RedisInstance,
    f func(c *RedisInstance, w http.ResponseWriter, r *http.Request)) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { f(c, w, r) })
}

Напишите ваши обработчики API следующим образом:

func AddTodoHandler(c *RedisInstance, w http.ResponseWriter, r *http.Request) {
    ...
}

Добавьте к мультиплексору вот так:

r.Handler("/todo", redisHandler(client, api.AddTodoHandler)).Methods("POST")

где client это экземпляр Redis.

Я бы рекомендовал использовать структуру App, которая инициализирует БД и Маршруты. И все методы Redis будут вызываться внутри. например type App struct{Routes *mux.Router, DB *DB_TYPE}

И который будет иметь App.initializeRoutes метод.

type App struct {
    Router *mux.Router
    DB     *redis.NewClient
}

func (a *App) Run(addr string) {
    log.Fatal(http.ListenAndServe(":8000", a.Router))
}


func (a *App) Initialize(addr, password string, db int) error {
    // Connect postgres

    db, err := redis.NewClient(&redis.Options{
      Addr:     addr,
      Password: password,
      DB:       db,
    })
    if err != nil {
        return err
    }

    // Ping to connection
    err = db.Ping()
    if err != nil {
        return err
    }

    // Set db in Model
    a.DB = db
    a.Router = mux.NewRouter()
    a.initializeRoutes()
    return nil
}


func (a *App) initializeRoutes() {
    a.Router.HandleFunc("/todo", a.AddTodoHandler).Methods("POST")
    a.Router.HandleFunc("/todo/{id}", a.GetTodoHandler).Methods("GET")
}

// AddTodoHandler has access to DB, in your case Redis
// you can replace the steps for Redis.
func (a *App) AddTodoHandler() {
   //has access to DB
   a.DB
}

Надеюсь, вы поймете, что вы можете даже извлечь работу по модели в отдельную структуру, а затем передать ее в функции

r.HandleFunc("/todo/{id}", redisHandler.api.GetTodoHandler).Methods("GET")

Ваш redisHandlerкак определено в main, не имеет api поле, так что это, естественно, не компилируется.

Если вы пересмотрели свой RedisInstance введите в api пакет, и вы определили методы-обработчики для этого типа в файлах, специфичных для метода, затем вы можете инициализировать ваш redisHandler используя это api.RedisInstance введите и вы можете удалить main.RedisInstance определение типа:

package main

import (
    "github.com/gorilla/mux"
    "github.com/go-redis/redis"
    "net/http"
    "fmt"
    "log"
    "encoding/json"
    "io/ioutil"
    "../redismanager"
    "../api"
)

func main() {

    //Initialize Redis Client
    client := redismanager.InitRedisClient()
    //Get current redis instance to get passed to different Gorilla-Mux Handlers
    redisHandler := &api.RedisInstance{RInstance:&client}

    //Initialize Router Handlers
    r := mux.NewRouter()
    r.HandleFunc("/todo", redisHandler.AddTodoHandler).Methods("POST")
    r.HandleFunc("/todo/{id}", redisHandler.GetTodoHandler).Methods("GET")

    fmt.Println("Listening on port :8000 . . .")

    // Bind to a port and pass our router in
    log.Fatal(http.ListenAndServe(":8000", r))

}
Другие вопросы по тегам