如何编写Go API:终极指南 - Jonny Langefeld

作者:API传播员 · 2025-11-12 · 阅读时间:5分钟

“TIL”(今天我了解到)是一个有趣的缩写词,意为“今天我学到了”。作为一名英语非母语者,我经常通过在线词典了解这些缩写词的含义。这个词让我意识到学习是无止境的,因此我开始撰写关于Python Flask API的博客,随后又扩展到如何编写Go API的系列文章。在这篇文章中,我将分享自己在编写高效且可用于生产的Go API过程中积累的最佳实践。


项目脚手架

以下是我推荐的项目结构布局,可以根据需要在 pkg 目录下添加更多模块:

├── go.mod
├── go.sum
├── main.go

# main.go 位于根目录,而非 /cmd 目录
├── pkg
│   ├── api# 包含所有 API 相关函数和路由
│   │   ├── api.go
│   │   ├── api_test.go
│   │   └── operations.go
│   ├── db# 数据库交互逻辑
│   │   ├── db.go
│   │   └── db_test.go
│   ├── middleware# 自定义中间件
│   │   ├── context.go
│   │   └── logger.go
│   └── types# 类型定义单独存放
│       └── types.go
├── readme.md
└── tools.go# 通过 go.mod 管理工具版本

在解决项目结构问题后,我们可以进一步探讨 main.go 文件的实现。


工具.go 模式

通过 Go 模块管理工具依赖关系是一种非常高效的方式。它不仅可以固定依赖版本,还能将其包含在供应商目录中。Marco Franssen 的博客对此有详细探讨。


带 pflag 的命令行标志

使用 pflag 可以轻松处理命令行标志,并提供内置帮助功能。例如:

$ go-api -h
用法:
  -a, --address string   API 监听的地址,格式为 "主机:端口"(默认值为 ":8080")

使用 zap 进行结构化日志记录

借助 zap,我们可以生成清晰且结构化的日志输出,便于调试和监控。


优雅的退出

在 API 关闭时,执行清理任务是一个良好的实践。通过在程序启动时创建一个通道监听特定事件(如键盘中断),可以实现优雅的退出。例如:

// 示例代码

启动时的日志版本

在调试日志中记录代码版本信息非常有用。通过在 main.go 文件中注入未设置的 version 变量,并在构建命令中设置该变量,可以实现这一功能:

var version string
// 在 main 函数中使用 version

在独立包中定义类型

将类型定义为可复用的结构体(struct),不仅有助于代码组织,还能方便其他开发者直接导入这些类型。例如,在 pkg/types/types.go 中定义类型:

type ExampleType struct {
    Field string json:"field"
}

使用 chi 作为 HTTP 框架

chi 是一个轻量级的 HTTP 路由框架,支持嵌套路由和中间件。以下是一个示例请求树,用于处理商店的商品和订单:

/api
  /products
  /orders

定制中间件

自定义中间件可以为 API 处理逻辑注入额外功能。例如,我们可以创建一个中间件,将数据库对象注入到请求上下文中,从而避免重复访问数据库:

func ArticleMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // 从数据库中获取文章对象并注入上下文
        ctx := context.WithValue(r.Context(), "article", article)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

分页

关于分页的详细实现,请参考相关博客文章。


与 gorm 的数据库集成

通过自定义客户端接口封装 gorm 客户端,可以简化数据库操作并提高测试的可维护性。例如:

type DBClient interface {
    Connect() error
    // 其他数据库操作方法
}

使用 dockertest 进行数据库集成测试

为了确保测试环境与生产环境一致,可以使用 dockertest 创建短生命周期的数据库容器。这种方法不仅适用于本地开发,也能在 CI 系统中稳定运行。


API 与 gomock 的集成测试

通过 gomock 创建模拟接口,可以在不依赖实际数据库的情况下测试 API。例如:

mockDB := gomock.NewController(t)

使用 http swagger 将文档作为代码

通过 Swagger UI,可以为 API 提供交互式文档。启动后,访问 <http://localhost:8080/swagger> 即可查看完整的 API 文档:

Swagger UI


分阶段的 Dockerfile

使用多阶段构建 Docker 镜像,可以优化构建过程。例如:

FROM golang:1.17 AS builder
WORKDIR /app
COPY . .
RUN go build -o go-api

FROM gcr.io/distroless/base
COPY --from=builder /app/go-api /bin/go-api
ENTRYPOINT ["/bin/go-api"]

通过以上实践,我们可以构建一个高效、可维护且适用于生产环境的 Go API。

原文链接: https://jonnylangefeld.com/blog/how-to-write-a-go-api-the-ultimate-guide