Golang将参数传递给Gorilla路由器

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

Golang pass arguments to gorilla router

问题

你可以在NewRouter函数中创建数据库连接,并将其作为参数传递给每个处理程序函数。这样,所有处理程序函数都可以共享同一个数据库连接。

首先,在NewRouter函数中创建数据库连接,并将其传递给每个处理程序函数。修改代码如下:

func NewRouter() *mux.Router {
    db, err := sql.Open("mysql", "psanker:123@/education_data")
    err = db.Ping()

    if err != nil {
        fmt.Println("Failed to prepare connection to database")
        log.Fatal("Error:", err.Error())
    }

    defer db.Close()

    router := mux.NewRouter().StrictSlash(true)
    for _, route := range routes {
        // 将数据库连接作为参数传递给处理程序函数
        route.HandlerFunc = withDB(route.HandlerFunc, db)

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

然后,创建一个辅助函数withDB,它接受处理程序函数和数据库连接作为参数,并返回一个新的处理程序函数,该函数在调用原始处理程序函数之前将数据库连接作为参数传递给它。修改代码如下:

func withDB(handler http.HandlerFunc, db *sql.DB) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        // 在调用原始处理程序函数之前将数据库连接作为参数传递给它
        handler(w, r, db)
    }
}

最后,在每个处理程序函数中接受数据库连接作为参数,并使用它进行数据库操作。修改代码如下:

func getDistrict(w http.ResponseWriter, r *http.Request, db *sql.DB) {
    vars := mux.Vars(r)
    districtId := vars["districtId"]
    fmt.Fprintln(w, "District id : ", districtId)
}

func getDistricts(w http.ResponseWriter, r *http.Request, db *sql.DB) {
    w.Header().Set("Content-Type", "application/json;charset=UTF-8")
    w.WriteHeader(http.StatusOK)
    rows, err := db.Query("SELECT * from districts")
    check(err)
    var district District
    for rows.Next() {
        var id int64
        test := "hey"
        district = District{Id: id, Activities: test}
    }

    if err := json.NewEncoder(w).Encode(district); err != nil {
        check(err)
    }
}

现在,每个处理程序函数都接受数据库连接作为参数,并可以使用它进行数据库操作。这样,它们将共享同一个数据库连接。

英文:

So I have two files. In one I have initialize a gorilla router, and register handlers. In the other I define the handlers. The handlers are supposed to query a MYSQL database. Routes.go looks like this -

    package main

import (
	"net/http"
	"github.com/gorilla/mux"
	"database/sql"
    _ "github.com/go-sql-driver/mysql"
    "fmt"
)

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

type Routes[]Route

func NewRouter() *mux.Router {
	db, err := sql.Open("mysql", "psanker:123@/education_data")
	err = db.Ping()

	if err != nil {
		fmt.Println("Failed to prepare connection to database")
		log.Fatal("Error:", err.Error())
	}

	defer db.Close()

	router := mux.NewRouter().StrictSlash(true)
	for _, route := range routes {
		router.
			Methods(route.Method).
			Path(route.Pattern).
			Name(route.Name).
			Handler(route.HandlerFunc)
	}
	return router
}

var routes = Routes{
	Route {
		"Index",
		"GET",
		"/",
		Index,
	},
	Route {
		"getDistrict",
		"GET",
		"/district/{districtId}",
		getDistrict,
		DBConn &db,
	},
	Route {
		"getDistricts",
		"GET",
		"/districts",
		getDistricts,
	},
}

My handlers.go file looks like this -

package main

import (
	"fmt"
	"net/http"
    "github.com/gorilla/mux"
    "encoding/json"
)

func Index(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "WELCOME!")
}

func getDistrict(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	districtId := vars["districtId"]
	fmt.Fprintln(w, "District id : ", districtId)
}

func getDistricts(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json;charset=UTF-8")
	w.WriteHeader(http.StatusOK)
	rows, err := db.Query("SELECT * from districts")
	check(err)
	var district District
	for rows.Next() {
		var id int64
		test := "hey"
		district = District{Id: id, Activities: test}
	}

	if err := json.NewEncoder(w).Encode(district); err != nil {
		check(err)
	}
}

I want to use one database connection for all of my handlers, how do I achieve this?

答案1

得分: 2

Go的sql.DB类型表示一个连接池而不是单个连接。建议在程序初始化时创建一个连接池,并且可以选择以下两种方式:

  1. 创建一个全局连接池并直接使用它(连接池可以安全地进行并发访问)
var db *sql.DB

func main() {
    var err error
    db, err = sql.Open("connection string here")
    if err != nil {
        // 处理错误
    }

    // 程序的其他部分/路由器/等等
}

func MyHandler(w http.ResponseWriter, r *http.Request) {
    err := db.Query("...")
    if err != nil {
        // 处理错误
    }

    // 处理程序的其他部分
}
  1. 创建满足http.Handler接口的自定义处理函数,并接受连接池或包含连接池的结构体作为额外参数。我在这里写了一篇文章介绍了这个方法:https://elithrar.github.io/article/custom-handlers-avoiding-globals/

(我可以提供另一个示例,但我现在在手机上,对不起排版可能不太好)

另外,可以查看sqlx来简化对结构体的查询和处理。

英文:

Go's sql.DB type represents a connection pool and not a single connection. It is recommended that you create a pool on program initialization and either:

  1. Create a global pool and just use it (the pool is safe for concurrent access)

     var db *sql.DB
    
     func main() {
     var err error
     db, err = sql.Open("connection string here")
     if err != nil {
     	// handle it
     }
    
     // Rest of program/router/etc
     }
    
     func MyHandler(w http.ResponseWriter, r *http.Request) {
     err := db.Query("...")
     if err != nil {
     	 // handle it
         }
    
         // Rest of handler
     }
    
  2. Create custom handler functions that satisfy http.Handler and accept the pool or a struct containing the pool as an additional argument. I wrote about that here: https://elithrar.github.io/article/custom-handlers-avoiding-globals/

(I would provide another example but I'm on mobile; excuse the poor indentation)

Also look at sqlx to simplify your query into/from struct handling.

答案2

得分: 1

Elithar的回答是实现这一目标的标准方法。如果你想在每个处理程序中注入一个数据库连接,这是一种解决方法。你需要定义自己的处理程序,并将其转换回路由器所期望的形式。以下是Elithar可能想要给出的示例代码。

func main() {
    http.HandlerFunc(myDbHandler(Index, db))
}

type dbHandler func(w http.ResponseWriter, r *http.Request, db *sql.DB)

func myDbHandler(handler dbHandler, db *sql.DB) func(w http.ResponseWriter, r *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        handler(w, r, db)
    }
}

func Index(w http.ResponseWriter, r *http.Request, db *sql.DB) {
    // 处理程序的代码放在这里
}

希望对你有帮助!

英文:

Elithar's answer is the standard way of achieving this. If you want to inject a db connection in every handler, this is one way to solve it. You define your own handler and convert it back to what the router expects. Here is the example, I assume, that Elithar wanted to give.

func main() {
    http.HandlerFunc(myDbHandler(Index, db))
}

type dbHandler func(w http.ResponseWriter, r *http.Request, db *sql.DB)

func myDbHandler(handler dbHandler, db *sql.DB) func(w http.ResponseWriter, r *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        handler(w, r, db)
    }
}

func Index(w http.ResponseWriter, r *http.Request, db *sql.DB) {
    // handler code goes here
}

huangapple
  • 本文由 发表于 2015年6月11日 06:52:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/30768684.html
匿名

发表评论

匿名网友

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

确定