【问题标题】:Pass a reference to a Redis instance to a Gorilla/Mux Handler将对 Redis 实例的引用传递给 Gorilla/Mux 处理程序
【发布时间】:2018-09-03 01:46:55
【问题描述】:

我在玩一些 Gorilla/MuxGo-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
}

ma​​in.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 包中的 Handler 进行此操作时遇到了一些困难。

例如,如果我在主目录中添加:

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 还是很陌生,即使经过多次研究和阅读,也没有找到任何更清洁的解决方案。

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

【问题讨论】:

  • 我建议使用初始化 DB 和路由的 App 结构。并且所有的 Redis 方法都会在里面被调用。例如type App struct{Routes *mux.Router, DB *sql.DB} 并且将有 App.initializeRoutes 方法

标签: go redis gorilla


【解决方案1】:

编写一个函数,将带有 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 实例。

【讨论】:

  • 这很干净。谢谢!
【解决方案2】:

我建议使用初始化 DB 和路由的 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
}

希望你明白这一点,你甚至可以将模型工作提取到一个单独的结构中,然后将它传递给 func

【讨论】:

    【解决方案3】:
    r.HandleFunc("/todo/{id}", redisHandler.api.GetTodoHandler).Methods("GET")
    

    你的redisHandler,在main 中定义,没有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() {
    
        //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))
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-09-13
      • 2020-08-26
      • 2015-04-07
      • 2014-11-28
      • 2015-04-02
      • 1970-01-01
      • 2014-12-22
      • 2015-03-25
      相关资源
      最近更新 更多