Best way to integrate database into Go Web application

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

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

}

huangapple
  • 本文由 发表于 2015年5月20日 04:50:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/30335865.html
匿名

发表评论

匿名网友

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

确定