欢迎回到我们激动人心的Golang后端开发系列的最后部分!🎉 到现在为止,你已经挥动你的编码魔杖,实现了数据库结构、包模型和CRUD操作。
在这个最后章节里,我们将深入讨论测试用例、Swagger文档、Docker配置、生产就绪,还要加上一点部署魔法。当然,我们还准备了一些笑话,因为没有笑声的调试简直就是找麻烦。
让我们来了解 Fiber:一个超快速的 Go 语言框架,设计用于快速开发并简化流程,注重性能和零内存分配。
- 小工具
让我们从助手函数开始,它们是我们在后端旅程中的好帮手。助手函数对于保持代码的 DRY(不要重复自己)原则和组织性非常重要。
他们可以处理诸如结构化API响应等重复性任务。结构化API响应、处理用户角色的中间件以及会话管理对于保持代码库的整洁与高效至关重要。这些组件能够确保您的应用程序组织有序、安全且具备可扩展性。
. API 响应格式化
创建一个标准的响应格式可以确保您的 API 端点保持一致。下面是一个用于 API 响应的辅助结构示例:
package helpers
// APIResponse 是一个用于处理 API 响应的结构
// Status 状态可以是 "success" 或 "error"
// Message 可以是任何字符串
// Data 可以是任何数据类型
// StatusCode 状态码可以是任何服务器状态码,比如 200, 400, 500
type APIResponse struct {
Status string `json:"status"`
Message string `json:"message"`
StatusCode int `json:"status_code"`
Data interface{} `json:"data"`
}
小提示:不要在任何地方硬编码响应格式。DRY(Don't Repeat Yourself)不仅是一种编码原则,更是一种编程哲学。
- 中间件(用户的角色)
中间件的功能可以用来管理用户角色和权限,确保只有经过授权的用户才能访问系统上的特定端点或接口。
package middleware
import (
"log"
"github.com/gofiber/fiber/v2"
)
// 角色中间件 检查用户是否有所需的角色之一
func RoleMiddleware(roles []string, errorMessage string) fiber.Handler {
return func(ctx *fiber.Ctx) error {
userRole := ctx.Locals("role")
// 这在身份验证期间设置
// 检查 userRole 是否在允许的角色中
for _, role := range roles {
if userRole == role {
return ctx.Next()
}
}
// 记录未经授权的访问尝试日志
log.Printf("未经授权的访问尝试,用户角色为: %v", userRole)
// 返回拒绝状态并带有自定义错误消息
return ctx.Status(fiber.StatusForbidden).JSON(fiber.Map{"message": errorMessage})
}
}
- 会话管理
管理用户会话对于保持状态至关重要,这可以确保用户能够享受流畅的体验。可以使用中间件在 GoFiber 中处理会话管理。
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/session"
)
var store *session.Store
func 初始化() {
store = session.New(session.Config{
Expiration: 4 * time.Hour, // 会话在4小时不活跃后过期
KeyLookup: "cookie:session", // 建议在使用TLS时使用__Host-前缀
CookieSecure: false,
CookieHTTPOnly: false,
CookieSameSite: "Lax",
Storage: sqlite3.New(),
})
}
func 主要() {
app := fiber.New()
app.Use(func(c *fiber.Ctx) error {
sess, err := store.Get(c)
if err != nil {
return err
}
defer sess.Save()
// 设置会话值为用户名
sess.Set("username", "john_doe")
// 获取并使用设置的用户名
username := sess.Get("username")
c.Locals("username", username)
return c.Next()
})
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello, " + c.Locals("username").(string))
})
app.Listen(":3000") // 启动服务器并监听3000端口
}
小贴士:通过采用这些实践,您可以使用GoFiber创建一个稳定且易于维护的后端。
2. 我们的 CRUD 操作的测试用例:
好的代码就是写好测试的代码。我们来做最有趣的部分:写测试。(开玩笑,其实,在午夜把未测试的功能部署到生产环境感觉更爽)😅
- 创建数据库测试记录。
package tests
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/your_project/models"
)
func TestCreateEntry(t *testing.T) {
db := setupTestDB() // 这里模拟数据库
defer db.Close()
newEntry := models.YourModel{Field1: "test", Field2: "value"}
result := db.Create(&newEntry)
assert.Nil(t, result.Error)
assert.NotZero(t, newEntry.ID)
}
- 从数据库测试读取记录。
package tests
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/your_project/models"
)
func TestReadEntry(t *testing.T) {
db := setupTestDB()
defer db.Close()
entry := models.YourModel{Field1: "测试"}
db.Create(&entry)
var fetchedEntry models.YourModel
db.First(&fetchedEntry, entry.ID)
assert.Equal(t, "test", fetchedEntry.Field1)
}
- 更新数据库测试记录: 更新数据库中的测试记录。
包 test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/your_project/models"
)
func TestUpdateEntry(t *testing.T) {
db := setupTestDB() // 在这里模拟数据库
defer db.Close()
// 创建一个将要更新的条目
entry := models.YourModel{Field1: "test", Field2: "value"}
db.Create(&entry)
// 更新条目
entry.Field1 = "updated"
result := db.Save(&entry)
assert.Nil(t, result.Error)
// 获取更新后的记录
var updatedEntry models.YourModel
db.First(&updatedEntry, entry.ID)
assert.Equal(t, "updated", updatedEntry.Field1)
}
测试更新条目功能:此测试功能会创建一个条目,对其进行更新,并通过检查更新字段值来验证更新是否成功。
- 从数据库中删除条目。
package tests
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/your_project/models"
"errors"
"gorm.io/gorm"
)
func TestDeleteEntry(t *testing.T) {
db := setupTestDB() // 初始化测试数据库
defer db.Close()
// 插入一个要删除的条目
entry := models.YourModel{Field1: "test", Field2: "value"}
db.Create(&entry)
// 删除条目:
result := db.Delete(&entry)
assert.Nil(t, result.Error) // 断言结果没有错误
// 尝试查找已被删除的条目
var deletedEntry models.YourModel
fetchResult := db.First(&deletedEntry, entry.ID)
assert.NotNil(t, fetchResult.Error) // 断言获取结果存在错误
assert.True(t, errors.Is(fetchResult.Error, gorm.ErrRecordNotFound)) // 断言 fetchResult.Error 是 gorm.ErrRecordNotFound 错误
}
测试删除条目:此测试功能会创建一个条目后,删除该条目,并验证该条目已被成功从数据库中删除。
3. 试试通过Swagger来测试我们的API。
Swagger 让 API 的文档编写和测试如此简单,你可能会想为什么你没有早点开始做这件事。让我们把 Swagger 集成到我们的 Golang 项目中。
安装 Swagger 在 Go 中
在 Go 中安装 Swagger 非常简单。你可以通过运行以下命令来安装:
使用 `go install github.com/swaggo/swag/cmd/swag@latest` 命令安装最新的 swag 工具
以下命令将安装最新版本的 Swagger,您可以使用它为 Go 项目等生成 Swagger 文档内容。
- 如何写 Swagger API
// @Summary 创建一个项目
// @Description 添加一个新的项目到数据库。此接口允许用户通过在请求正文中提供所需数据来创建新项目。
// @Accept json // @Produce json
// @Param item body models.YourModel true "项目数据"
// 需要添加到数据库中的项目数据
// @Success 200 {object} models.YourModel
// 返回新创建的项目
// @Failure 400 {object} fiber.Map
// 如果请求无效,将返回错误信息
// @Failure 500 {object} fiber.Map
// 如果出现内部服务器错误,将返回错误信息
// @Router /api/v1/items [post]
func CreateItem(ctx *fiber.Ctx) error {
// 函数逻辑
}
通过添加这些注释,你可以为你的应用程序的API生成全面的Swagger说明文档,让别人更轻松地理解和使用你的端点。
- 使用Swagger
要为您的 Go 项目生成 Swagger 文档(或 Swagger 规范),可以运行以下命令即可。
swag init
此命令将扫描您的 Go 项目中的注释并生成 Swagger 文档文件,通常位于 /swagger/index.html
目录中。这使得记录和测试您的 API 变得更加简单。
- 尝试用Swagger测试我们的API
要使用 Swagger 并通过 Swagger UI 测试您的 API,请按照以下步骤进行即可。
go run main.go
查看 Swagger 文档:打开网络浏览器并前往:<具体网址>
/swagger/index.html
这将让你使用 Swagger UI 查看和测试你的 API 文档内容。
4. 生产准备阶段。
服务器生产惯例——那些神奇的技巧,即使在流量激增或有人不小心运行了一个没有正确退出条件的for
循环时,也能让您的后端保持稳定运行。
你的服务器不是个只会消耗CPU和内存资源的黑洞,它需要合理的资源管理。
- 数据库的实际操作
- 启用连接池功能。
- 使用环境变量存储敏感数据。
- 定期备份 — 因为不小心删除了生产数据是件很糟糕的事。
dbUser := os.Getenv("DB_USER") // 从环境变量获取数据库用户名
dbPass := os.Getenv("DB_PASS") // 从环境变量获取数据库密码
dbName := os.Getenv("DB_NAME") // 从环境变量获取数据库名
dsn := fmt.Sprintf("%s:%s@/%s?charset=utf8mb4&parseTime=True&loc=Local", dbUser, dbPass, dbName)
// 使用用户名、密码和数据库名生成数据库连接字符串
- 停止生产实践。
- 优化资源利用如Goroutines:Goroutines虽然轻量,但如果创建过多,可能会占用过多的CPU和内存资源。
- 优化内存:使用高效的数据结构,并尽可能避免大的内存分配。尽量使用切片而不是数组,可以考虑使用
sync.Pool
来复用分配。
- 让我们的 Go 代码一直运行着。
- 使用反向代理:将您的 Golang 服务器部署在像 NGINX 这样的反向代理后面,以实现负载均衡、缓存和增强的安全性。
- 监控和记录一切:生产服务器就像脾气暴躁的青少年——除非你注意,否则它们不会告诉你哪里出了问题。监控并记录每一个重要的事件。
- 使用优雅的终止:在部署新版本或停止服务器时,确保现有的请求能够优雅地终止。
小贴士:好好对待你的服务器,它会以稳定的运行时间和安心回报你。记住:服务器在凌晨三点崩溃是大自然在提醒你,“你得写更好的日志。”
5. 使用 CI/CD 部署.
不使用 CI/CD 部署就像没有版本控制就开发一样。这会引入不必要的风险、低效和潜在错误。还是不要这样做。
CI/CD流水线简介CI/CD — 用简单易懂的话来说,就是频繁地将准备好的变更交付给业务侧的过程中示例:生产用的Dockerfile
# 从基础镜像开始
FROM golang:1.20-alpine
# 设置工作目录
WORKDIR /app
# 首先复制 go.mod 和 go.sum 文件以便利用 Docker 缓存
COPY go.mod go.sum ./
RUN go mod download
# 复制应用其余的代码
COPY . .
# 构建 Go 应用程序
RUN go build -o main .
# 暴露应用在 8080 端口
EXPOSE 8080,
# 运行应用的指令
CMD ["./main"]
使用Docker Compose进行部署并使用GitHub Actions或Jenkins设置CI/CD管道,你可以确保GoFiber应用程序的平稳和自动部署流程。
6. 结论部分
恭喜,你已经构建了响应的结构,搭建了中间件,编写了测试用例,使用Swagger对API进行了文档化,并为上线做好了准备。下一步?像大佬一样部署和扩展。
开发 Golang 后端数据库 API 的最佳实践(第一篇)本文将介绍构建后端 API 的几个重要方面。从标准编码约定,数据共享方式……medium.com Golang 后端数据库 API 开发的最佳实践(第二部分)本文系列将涵盖后端 API 开发的重要部分。从搭建我们的 API 服务器,创建我们的模式……medium.com有反馈或编程笑话吗?在评论区留言吧!继续写代码,记得:唯一不变的就是变化,还有 nil
指针。
共同学习,写下你的评论
评论加载中...
作者其他优质文章