使用 Go 和 TDD 构建财务跟踪 REST API - 第 1 部分
使用 Go 构建一个简单的 REST API 总是很有挑战性的,从设计架构到实现它,因为没有标准的方法来实现它,并且开发人员(软件架构)可以自由地做任何他们想做的事情,但这需要付出代价。
在使用 RubyOnRails 构建服务和 REST API 近六年后,我意识到开发人员热衷于抽象,但这并不总是一个好的选择。例如,如果你观察一下几乎每个 RubyOnRails 项目,就会发现其中存在太多的抽象和隐式操作,这使得学习曲线更加陡峭,入门难度也相对较低。
今天,我将向您展示使用 Go 设计 REST API 的不同视角,它就像一位好父母,不让我们做任何事情,而是教会我们练习、耐心和毅力。
让我们开始吧,首先每个 Go 项目都必须在 中创建$GOPATH
。我的$GOPATH
目录位于,
~/go/src/
~/go/
。但我们将在下创建项目,~/go/src/github.com/budget-api
以便能够使用下载项目go get
。
因此我们将在 CLI 中运行这些命令。
cd ~/go/src/github.com/azbshiri
mkdir budget-api
touch main.go
(如果没有,您可以在将目录更改为后github.com/username
使用来创建它)mkdir -p github.com/username
~/go/src/
好的,我们将使用 PostgreSQL 作为数据库,并使用 Gorilla 工具包来构建 RESTful API。目前,我们只需要github.com/go-pg/pg
能够连接到 PostgreSQL 并github.com/gorilla/mux
进行路由。
go get github.com/go-pg/pg
go get github.com/gorilla/mux
好了,现在我们有了先决条件,我们应该考虑如何使用它们并设计架构。
我将使用一个struct
作为控制器/处理程序操作的容器,server
以便能够访问数据库、路由器等。因此我创建了一个名为的新文件server.go
并将声明放入其中。
// server.go
package main
type server struct {
db *pg.DB
mux *mux.Router
}
并创建一个工厂函数来初始化一个新服务器(这将有助于测试,我将在将来展示)
// server.go
package main
type server struct {
db *pg.DB
mux *mux.Router
}
func newServer(db *pg.DB, mux *mux.Router) *server {
s := server{db, mux}
s.routes() // register handlers
return &s
}
我们需要routes.go
声明 API 的路径
// routes.go
func (s *server) routes() {
// register handlers here
}
现在我们有了如下的文件层次结构
.
├── main.go
├── rotues.go
└── server.go
为了能够运行服务器,我们应该实现http.Handler
接口,因此我们添加了一个新的指针函数来server
调用ServeHTTP
并在路由器中为注册的处理程序提供服务(mux
)
// server.go
package main
import (
"net/http"
"github.com/go-pg/pg"
"github.com/gorilla/mux"
)
type server struct {
db *pg.DB
mux *mux.Router
}
func newServer(db *pg.DB, mux *mux.Router) *server {
server := server{db, mux}
server.routes() // register handlers
return &server
}
func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
s.mux.ServeHTTP(w, r)
}
现在我们可以更新main.go
文件来运行服务器
package main
import (
"net/http"
"github.com/go-pg/pg"
"github.com/gorilla/mux"
)
func main() {
db := pg.Connect(&pg.Options{
User: "alireza",
Password: "alireza",
})
mux := mux.NewRouter()
server := newServer(db, mux)
http.ListenAndServe(":8080", server)
}
此时,我们没有任何注册的处理程序可以连接到 API,因此我们只有一个无用的运行服务器,但我们有标准,这对于加快开发过程来说是一件好事。
由于我是 TDD 专家,所以我们先从测试开始,编写我们的第一个 API。如你所知,我们将构建一个财务跟踪 REST API,所以我们需要定义Budget
业务模型并调用 API 端点/budgets
。
// budgets_test.go
package main
import (
"net/http"
"os"
"testing"
"github.com/azbshiri/common/test"
"github.com/go-pg/pg"
"github.com/gorilla/mux"
"github.com/stretchr/testify/assert"
)
var testServer *server
func TestMain(m *testing.M) {
testServer = newServer(
pg.Connect(&pg.Options{
User: "alireza",
Password: "alireza",
Database: "alireza",
}),
mux.NewRouter(),
)
os.Exit(m.Run())
}
func TestGetBudgets(t *testing.T) {
res, err := test.DoRequest(testServer, "GET", "/budgets", nil)
assert.NoError(t, err)
assert.Equal(t, res.Code, http.StatusOK)
}
为了编写上述测试,我们使用了两个库github.com/stretchr/testify/assert
,github.com/azbshiri/common/test
第一个库用于使断言更容易,第二个库有点复杂,我自己构建了它以减少初始化 ahttp.Request
和 a 的重复httptest.ResponseRecorder
,并将它们传递给现有的路由器以便能够捕获响应(这是在 Go 中编写集成测试的正常方式,有关更多信息,您可以查看https://golang.org/src/net/http/httptest/example_test.go)。
运行测试
go test -v .
=== RUN TestGetBudgets
-------- FAIL: TestGetBudgets (0.00s)
budgets_test.go:42:
Error Trace: budgets_test.go:42
Error: Not equal:
expected: 404
actual : 200
Test: TestGetBudgets
FAIL
exit status 1
FAIL github.com/azbshiri/budget-api 0.018s
shell returned 1
呼!本来计划在一篇文章里涵盖所有内容,但说实话,这篇文章比预期的要长,所以请允许我把它分成一系列文章。
再次感谢您的阅读,请不要忘记在下面的评论中留下您的问题和/或建议。
鏂囩珷鏉ユ簮锛�https://dev.to/alirezabashiri/building-a-finance-tracking-rest-api-using-go-with-test-driven-development---part-1-16o8