英文:
Best way to integrate database into Go Web application
问题
我是新手,正在学习使用Go开发Web应用程序。
我正在寻找将MySQL数据库集成到我的Web应用程序中的最佳方法。
我考虑做这样的事情:
type Context struct {
Database *sql.DB
}
// 这里有一些Context结构的数据库方法,比如Close()和Query()
在我的Web应用程序的主函数中,我会这样写:
db := sql.Open(...)
ctx := Context{db}
然后,我将把我的Context
结构传递给需要数据库连接的各个处理程序。这样的设计决策是否合适,还是有更好的方法将SQL数据库集成到我的Web应用程序中?
英文:
I'm new to developing web applications in Go.
I'm looking for the best way to integrate a MySQL database into my web application.
I was thinking of doing something like this:
type Context struct {
Database *sql.DB
}
// Some database methods like Close() and Query() for Context struct here
In the main function for my web application I would have something like this:
db := sql.Open(...)
ctx := Context{db}
I would then pass then pass my Context
structure into various handlers that require a database connection. Would this be a good design decision or is there a better way to integrate a SQL database into my web application?
答案1
得分: 3
我通常会这样做:
package main
func main() {
db, err := sql.Open(...)
if err != nil {
log.Fatal(err)
}
defer db.Close()
http.HandleFunc("/feed", server.FeedHandler(db))
http.HandleFunc("/gui", server.GuiHandler(db))
...
log.Fatal(http.ListenAndServe(":8000", nil))
}
其中server
是一个单独的包,在其中定义、实现和测试所有的HTTP处理程序。
这基本上就是你想的那样,但跳过了将db封装在结构体中的步骤,这是不必要的。我不建议将db作为全局变量。拥有全局依赖项将完全破坏以可靠方式测试HTTP处理程序的可能性。
像上面的示例中那样进行依赖注入db,每次调用函数时需要多输入两个字符,但它允许您使用go-sqlmock包轻松测试您的HTTP处理程序,这是您在Web应用程序增长时肯定想要做的。
package server
func TestFeedHandler(t *testing.T) {
mockdb, err := sqlmock.New()
if err != nil {
t.Errorf("在打开存根数据库连接时出现意外错误'%s'", err)
}
columns := []string{"id", "name"}
sqlmock.ExpectQuery("SELECT id,name from persons;").
WillReturnRows(sqlmock.NewRows(columns).
AddRow(1234, "bob"))
handler := FeedHandler(mockdb)
// 测试处理程序返回预期结果
}
英文:
I typically do something like this:
package main
func main(){
db,err := sql.Open(...)
if err != nil {
log.Fatal(err)
}
defer db.Close()
http.HandleFunc("/feed", server.FeedHandler(db))
http.HandleFunc("/gui", server.GuiHandler(db))
...
log.Fatal(http.ListenAndServe(":8000", nil))
}
Where server
is a separate package where I define, implement and test all my http handlers.
This is basically what you were thinking of but skipping the step of wrapping the db in a struct which isn't necessary. I would not recommend making the db a global variable. Having global dependencies will completely destroy any possibility of testing your http handlers in a reliable fashion.
Dependency injecting the db, as in the example above, costs you two extra characters to type every time you call the function but it allows you to test your http handlers easily using the go-sqlmock package which you definitely want to do as your web app grows.
package server
func TestFeedHandler(t *testing.T){
mockdb, err := sqlmock.New()
if err != nil {
t.Errorf("An error '%s' was not expected when opening a stub database connection", err)
}
columns := []string{"id", "name"}
sqlmock.ExpectQuery("SELECT id,name from persons;").
WillReturnRows(sqlmock.NewRows(columns).
AddRow(1234, "bob")
handler := FeedHandler(mockdb)
// test that handler returns expected result
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论