Передать ссылку на экземпляр 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))
}