douguai7291
douguai7291
2017-04-29 12:39

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

已采纳

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:

enter image description here

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?

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

3条回答

  • du42561 du42561 4年前

    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.

    点赞 评论 复制链接分享
  • douzhi3779 douzhi3779 4年前
    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))
    
    }
    
    点赞 评论 复制链接分享
  • duanba8173 duanba8173 3年前

    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

    点赞 评论 复制链接分享