【问题标题】:Go using mux Router - How to pass my DB to my handlersGo using mux Router - 如何将我的数据库传递给我的处理程序
【发布时间】:2016-02-12 07:57:23
【问题描述】:

目前,我尝试使用 Go 创建一个小型 Web 项目,用于在服务器上处理数据。

我尝试将我的数据库连接传递给我的 HandlerFunc(tions),但它没有按预期工作。我对golang很陌生,所以可能我不了解这个lang的一些基本原理。

我的主要功能如下所示:

func main() {

    db, err := config.NewDB("username:password@/databasename?charset=utf8&parseTime=True")
    if err != nil {
        log.Panic(err)
    }   
    env := &config.Env{DB: db} 

    router := NewRouter(env)
    log.Fatal(http.ListenAndServe(":8080", router))
}

我的路由器:

func NewRouter(env *config.Env) *mux.Router {
    router := mux.NewRouter().StrictSlash(true)
    for _, route := range routes {
        var handler http.Handler

        handler = route.HandlerFunc
        handler = Logger(handler, route.Name)

        router.
            Methods(route.Method).
            Path(route.Pattern).
            Name(route.Name).
            Handler(handler)
    }   
    return router
}

还有我的路线:

type Route struct {
    Name        string
    Method      string
    Pattern     string
    HandlerFunc http.HandlerFunc
}

type Routes []Route

var routes = Routes{
    Route{
        "Index",
        "GET",
        "/",
        controller.Index,
    },  
    Route{
        "Show",
        "GET",
        "/todos/{todoId}",
        controller.TodoShow,
    },  
    Route{
        "Create",
        "POST",
        "/todos",
        controller.TodoCreate,
    },  
}

那么 - 我怎样才能将我的“env”(或 env.DB)传递给我的 FuncHandlers?我尝试了很多东西,但都没有奏效。

【问题讨论】:

    标签: go router mux


    【解决方案1】:

    您有三个选择:

    1. 使您的数据库连接池成为全局的,这样您就不必通过它了。 sql.DB 对并发访问是安全的,这是最简单的方法。不利的一面是,它使测试变得更加困难,并且混淆了池的“来源”——例如

      var db *sql.DB
      
      func main() {
          var err error
          db, err = sql.Open(...)
          // Now accessible globally, no need to pass it around
          // ...
       }
      
    2. 将您的处理程序包装在一个闭包中,以便内部处理程序可以访问它。您需要调整它以适应您的 range-over-routes 方法——这在 IMO 中有点迟钝,并且更难看出存在哪些路线,但我离题了——例如:

      func SomeHandler(db *sql.DB) http.HandlerFunc {
          fn := func(w http.ResponseWriter, r *http.Request) {
              res, err := db.GetThings()
              // etc.
          }
      
          return http.HandlerFunc(fn)
      }
      
      func main() {
          db, err := sql.Open(...)
          http.HandleFunc("/some-route", SomeHandler(db))
          // etc.
      }
      
    3. 创建一个接受处理程序的自定义处理程序类型 - 例如

      type AppHandler struct {
          Handler func(env *config.Env, w http.ResponseWriter, r *http.Request)
          Env *config.Env
      }
      
      // ServeHTTP allows your type to satisfy the http.Handler interface.
      func (ah *AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
          ah.Handler(ah.Env, w, r)
      }
      
      func SomeHandler(env *config.Env, w http.ResponseWriter, r *http.Request) {
          res, err := env.DB.GetThings()
          // etc.
      }
      

    请注意(无耻的插件!)我有 written about the last approach in detail,Alex Edwards 也有一个 excellent blog post,关于在 Go 程序中访问数据库池的方法。

    我能给出的唯一严格建议是,您应该避免在请求上下文中传递数据库池,这是低效且不好的做法(请求上下文是针对临时的、按请求的对象)。

    【讨论】:

    • 非常感谢,我已经看过 Alex Edwards 的博文并尝试实现他的闭包解决方案(使用他在 gist.github.com/alexedwards/5cd712192b4831058b21 的示例)现在,我更改了它并在我的模型包,它正在工作。我仍然不确定它是否是我项目的最佳解决方案,但目前它会满足我的需求。
    • 谢谢!刚开始使用 Golang 和 GORM,并且一直在寻找大约一个小时来寻找将我的 db 对象放入我的处理程序的“正确”方法,以便它们可以访问数据库。
    【解决方案2】:

    您始终可以将“env”定义为global variable

    但在大家讨厌我之前,这不是一个好的解决方案!您应该创建一个包,使用公共函数封装对数据库的访问,说明您的确切意图。

    类似

    Package db
    
    var config ....
    
    func ShowTodos(params ... ) result {
       your database access code here.... 
    }
    

    并从您的路由器功能访问它

    db.ShowTodos(...)
    

    【讨论】:

      猜你喜欢
      • 2014-11-30
      • 2015-03-25
      • 2021-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-17
      • 1970-01-01
      相关资源
      最近更新 更多