将一个 Redis 实例的引用传递给 Gorilla/Mux 处理程序。

huangapple go评论89阅读模式
英文:

Pass a reference to a Redis instance to a Gorilla/Mux Handler

问题

我正在尝试使用Gorilla/MuxGo-Redis来进行一些实践,但是我在这里遇到了一个小的实现问题。

基本上,我的项目结构如下所示:

将一个 Redis 实例的引用传递给 Gorilla/Mux 处理程序。

其中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还不太熟悉,即使经过多次研究和阅读,也没有找到更清晰的解决方案。

我的方法正确吗?还有更好的方法吗?

英文:

I'm trying to get my hands dirty while playing with some Gorilla/Mux and Go-Redis but I'm facing a little implementation problem here.

Essentially I have a project structured like the following:

将一个 Redis 实例的引用传递给 Gorilla/Mux 处理程序。

Where redismanager.go handles the initialization of a Redis Client:

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
}

Where main.go calls redismanager.InitRedisClient and initializes 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))

}

Now, I can easily define and let work properly AddTodoHandler in the same file like:

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

But, to make things a bit more modular, I'm trying to move all of these RouteHandlers inside their respective files in api package. In order to make that, I need to pass a reference to redisHandler but I'm having some difficulties when trying to make that with an Handler inside api package.

For instance, If in the main I add:

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

with 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
    }
}

It works nicely.

I'm still pretty new to Go and haven't found any cleaner solution to that even after several researches and reads.

Is my approach correct or are there any better ones?

答案1

得分: 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) {
    ...
}

像这样添加到mux中:

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

其中client是Redis实例。

英文:

Write a function that converts a function with the Redis instance argument to an HTTP handler:

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

Write your API handlers like this:

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

Add to the mux like this:

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

where client is the Redis instance.

答案2

得分: 2

我建议使用一个App结构体来初始化DB和Routes,并在其中调用所有的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 {
    // 连接到Redis
    db, err := redis.NewClient(&redis.Options{
        Addr:     addr,
        Password: password,
        DB:       db,
    })
    if err != nil {
        return err
    }

    // 检查连接
    err = db.Ping().Err()
    if err != nil {
        return err
    }

    // 设置DB到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可以访问DB,对于你的情况是Redis
// 你可以替换为Redis的步骤
func (a *App) AddTodoHandler() {
    // 可以访问DB
    a.DB
}

希望你明白了,你甚至可以将Model的工作提取到一个单独的结构体中,然后将其传递给函数。

英文:

I would recommend using an App struct which initializes DB and Routes. And all Redis methods will be called inside.
e.g. type App struct{Routes *mux.Router, DB *DB_TYPE}

And which will have App.initializeRoutes method.

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
}

Hope you get the point, you can even extract out the Model work into a separate Struct and then pass it inside func's

答案3

得分: 1

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

您的redisHandlermain中定义,没有api字段,因此自然无法编译。

如果您在api包中重新定义了RedisInstance类型,并在特定于方法的文件中定义了处理程序方法,则可以使用该api.RedisInstance类型初始化您的redisHandler,并且可以删除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() {

//初始化Redis客户端
client := redismanager.InitRedisClient()
//获取当前的redis实例以传递给不同的Gorilla-Mux处理程序
redisHandler := &api.RedisInstance{RInstance:&client}

//初始化路由处理程序
r := mux.NewRouter()
r.HandleFunc("/todo", redisHandler.AddTodoHandler).Methods("POST")
r.HandleFunc("/todo/{id}", redisHandler.GetTodoHandler).Methods("GET")

fmt.Println("监听端口:8000 . . .")

//绑定到端口并传递我们的路由器
log.Fatal(http.ListenAndServe(":8000", r))

}

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

Your redisHandler, as defined in main, has no api field, so this naturally doesn't compile.

If you re-defined your RedisInstance type in the api package, and you defined the handler methods on that type in the method-specific files, then you can initialize your redisHandler using that api.RedisInstance type and you can delete the main.RedisInstance type definition:

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

}

huangapple
  • 本文由 发表于 2017年4月29日 20:39:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/43695832.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定