使用 Go 和 TDD 构建财务跟踪 REST API - 第 1 部分

2025-06-08

使用 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
Enter fullscreen mode Exit fullscreen mode

(如果没有,您可以在将目录更改为后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
Enter fullscreen mode Exit fullscreen mode

好了,现在我们有了先决条件,我们应该考虑如何使用它们并设计架构。

我将使用一个struct作为控制器/处理程序操作的容器,server以便能够访问数据库、路由器等。因此我创建了一个名为的新文件server.go并将声明放入其中。

// server.go
package main

type server struct {
    db *pg.DB
    mux *mux.Router
}
Enter fullscreen mode Exit fullscreen mode

并创建一个工厂函数来初始化一个新服务器(这将有助于测试,我将在将来展示)

// 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
}
Enter fullscreen mode Exit fullscreen mode

我们需要routes.go声明 API 的路径

// routes.go
func (s *server) routes() {
    // register handlers here
}
Enter fullscreen mode Exit fullscreen mode

现在我们有了如下的文件层次结构

.
├── main.go
├── rotues.go
└── server.go
Enter fullscreen mode Exit fullscreen mode

为了能够运行服务器,我们应该实现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)
}
Enter fullscreen mode Exit fullscreen mode

现在我们可以更新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)
}
Enter fullscreen mode Exit fullscreen mode

此时,我们没有任何注册的处理程序可以连接到 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)
}
Enter fullscreen mode Exit fullscreen mode

为了编写上述测试,我们使用了两个库github.com/stretchr/testify/assertgithub.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
Enter fullscreen mode Exit fullscreen mode

呼!本来计划在一篇文章里涵盖所有内容,但说实话,这篇文章比预期的要长,所以请允许我把它分成一系列文章。

再次感谢您的阅读,请不要忘记在下面的评论中留下您的问题和/或建议。

鏂囩珷鏉ユ簮锛�https://dev.to/alirezabashiri/building-a-finance-tracking-rest-api-using-go-with-test-driven-development---part-1-16o8
PREV
注意数组 - V8 引擎建议
NEXT
React 状态的故事。