如何使用Go Fiber和Gorm框架来运行Golang应用程序
Go是一种通用的语言。你可以用Go来构建Web应用、微服务、云服务、API、DevOps工具,以及任何应用。
Go Fiber是一个受Express启发的Golang框架。Go Fiber是一个建立在快速HTTP之上的网络框架。它可以用来处理路由/端点、中间件、服务器请求等操作。
在本教程中,我们将进一步了解Go Fiber。然后,我们将使用Go Fiber与Gorm和一个SQLite数据库来建立一个Todo应用程序。
前提条件
要继续学习本教程,请确保你有 Golang 的基本知识。
这包括
- 在你的电脑上安装了Go,运行
go version命令来验证Go的安装。 - 能够设置基本的Golang应用程序。
- 运行和创建Golang应用程序,并了解如何编写Golang代码。
设置Go项目
像标准的基本应用程序一样,创建一个项目文件夹,用你喜欢的文本编辑器打开它。我将在本教程中使用Visual studio代码。
Go使用不同的库和框架。因此,在创建应用程序时,你需要在你的项目中保存这些包的二进制文件,以便你的应用程序能够访问它们。
Go也使用文件系统来保存模块的依赖性。这样就把Go模块与模块路径和与当前安装的模块相关的sematic版本保存起来。然后我们将这些模块导入到项目根目录下的应用程序中。
Go使用go.mod 和go.sum来管理这些依赖关系。go.mod 包含所有你安装的间接依赖关系和你想使用的版本。间接依赖不在项目内部使用,但被视为间接依赖。
另外,任何在go.mod 包中提到但在任何源文件中没有发现的依赖也被认为是间接依赖。go.sum 维护成功安装的包的校验。
如果你重新运行该项目,它不会安装所有的包。它通过将包的缓存存储在$GOPATH/pkg/mod 目录中来做到这一点。一个比较的例子是在使用Node.js和NPM时。在这种情况下,package.json 和package-lock.json 文件被用来管理 Node.js 的依赖性。
要初始化这些文件,在你的项目根目录下运行以下命令。
go mod init go-fiber-app
用下面的命令将创建一个go.mod 。
module go-fiber-app
go 1.17
在这种情况下,go-fiber-app 将是我们的直接模块和用于维护版本控制的模块声明。go-fiber-app 作为一个URL,用于在我们的应用程序中导入本地模块。go 1.17 是当前在你的计算机上运行的go版本。go.sum 将在之后我们开始安装我们的包时创建。
建立一个基本的Go Fiber服务器
让我们跳进去,用Go建立我们的第一个HTTP服务器,了解Go Fiber的最基本概念。就像Express一样,使用Fiber框架来启动你的第一个Go服务器是很简单的。
Go Fiber是一个受Express启发的框架。因此,让我们退一步,看看我们如何用Express创建一个简单的Node.js服务器。下面是一个利用Express框架的基本服务器。
// add Express library
const express = require("express");
// create a new Express instance
const app = express();
// Create a new endpoint
app.get("/", (req, res) => {
// send text
res.send("Hello World!");
});
// Start server on port 3000
app.listen(3000);
现在用Golang并使用Fiber框架,上面的Node.js例子工作起来也是一样的。只是一些语法发生了变化。同样的HTTP服务器被实现了,但是用不同的语言使用不同的框架。
让我们深入了解一下,看看我们如何用Go和Fiber创建一个外观相似的服务器。
首先,我们需要使用go get命令使Fiber对我们的应用程序可用。
让我们通过运行以下命令来安装它。
go get -u github.com/gofiber/fiber/v2
现在我们可以开始实现我们的第一个Go Fiber启发的HTTP服务器。继续前进,在你的项目文件夹中创建一个main.go 文件,然后按照以下步骤进行。
- 添加主模块。
主模块包含在每个Go文件中。它将模块导入到Go本地文件中的其他模块。
package main
- 由于我们使用的是Fiber,我们需要导入包来访问Go Fiber的函数和方法。
// add Fiber package
import "github.com/gofiber/fiber/v2"
- 添加一个main函数。
main函数是一个特殊的Go方法,定义了Golang应用程序的入口点。它被用来执行其他显式函数或Golang代码块。
func main() {
// create a new Fiber instance
app := fiber.New()
在main 函数中,我们要创建一个新的Fiber实例。这将实例化一个Fiber应用程序。
- 创建一个端点。
app.get 将设置我们的默认路由函数。它将接受一个 的Fiber上下文,并且它期待一个错误。这个 结构有各种很酷的东西。在我们的例子中,我们要发送一个纯文本字符串。context context
// Create a new endpoint
app.Get("/", func(c *fiber.Ctx) error {
// send text
return c.SendString("Hello, World!")
})
- 定义服务器的端口。
然后设置一个端口号,我们的服务器将监听并在本地运行。
// Start server on port 3000
app.Listen(":3000")
}
该应用程序已经准备好了,我们现在可以通过运行以下命令来测试它。
go run main.go
输出。

这就是你的成果。你最简单的Go Fiber HTTP服务器已经建立并运行。导航到http://127.0.0.1:3000/ ,浏览器上会显示一个简单的 "hello world "文本。
设置一个Fiber todos应用程序
以上是一个简单的HTTP服务器。现在让我们深入到一个更深刻的用例中,探索Go Fiber框架的更多内容。我们将使用一个带有SQLite数据库的todo应用程序用例来建立一个todos应用程序。
让我们从创建一个项目目录开始。然后,用go mod init go-fiber-todos 来初始化Go。
我们将安装以下软件包。
- Gorm
go get -u gorm.io/gorm
- Gorm SQLite驱动
go get -u gorm.io/driver/sqlite
- 谷歌UUID
go get github.com/google/uuid
- 空气
你还记得Node.js服务器的Nodemon吗?Air的工作原理与Nodemon基本相同。
当你在建立一个服务器时,你可能需要看管你的文件。这样一来,你只需启动一次你的服务器。然后,当你对你的文件进行修改时,服务器又会自动重新启动。
Air在Go的开发包中。每当你修改你的代码时,它就会实时重新加载你的Go服务器。所以你可以把它设置好,然后专注于你的代码。
go get -u github.com/cosmtrek/air
为我们的项目设置Air
我们需要设置Air,以便应用程序能够处理实时重载。Air会看管项目文件和目录,构建,并通过给我们提供一些彩色的日志输出来运行应用程序。

为了初始化Air的运行。
air init
上述命令将创建一个.air.toml 文件到当前目录,并带有默认设置。从那里,你可以直接运行air ,开始看管你的开发服务器。或者运行air -d ,在调试环境下打印所有日志。
.air.toml 是可配置的。你可以定制它的参数以满足你的要求。
设置数据库
我们正在使用Gorm来设置我们的SQLite数据库驱动。Gorm也利用go-sqlite3包来设置SQLite数据库。
go-sqlite3 是 框架。 是Go编程语言中的一个模块,可以创建引用C代码的包。这意味着,如果你使用 构建你的Go应用程序,你需要GCC(GNU编译器集合)。CGo CGo go-sqlite3
GCC编译器是GNU项目的一个优化版本,可以编译各种编程语言,如C代码。
[
go-sqlite3文档]中指出 在你用go install github.com/mattn/go-sqlite3(需要GCC)构建和安装go-sqlite3之后,你可以在将来不依赖GCC来构建你的应用程序。
然而,在我们的案例中,go-sqlite3 是通过Gorm SQLite驱动安装的。因此,我们需要建立一个GCC环境。让我们下载GCC MinGW-w64,用于32和64位Windows操作系统。然后,将MinGW-w64安装到你的笔记本电脑中。
在安装时,确保你选择以下架构。

安装完成后,添加GCC环境变量。在系统变量部分找到PATH环境变量,添加GCC bin,即C:\Program Files\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin 。
确保该路径与已安装的GCC的bin路径一致。为了应用这些变化,你需要重新启动你的计算机。一旦完成,在你的终端运行这个命令来检查这些变化是否被实施。
gcc
如果GCC被设置了,你会得到这个终端输出。

我们现在准备建立我们的SQLite数据库。
在你的项目根目录下,创建一个文件夹并命名为database 。在这个文件夹中,创建一个database.go 文件。让我们开始编码。
一个database 模块,我们将用来导入到其他本地模块。该模块负责维护与数据库的连接和操作。
// the database module
package database
导入SQLite驱动和Gorm。这将负责创建我们的SQLite数据库。
// import packages
import (
_ "gorm.io/driver/sqlite"
"gorm.io/gorm"
)
为Gorm连接器创建一个数据库实例。
var (
// Database instance => DB Gorm connector
DBConn *gorm.DB
)
创建todos
在根目录下,创建一个todos 文件夹。在这个文件夹中,创建一个todos.go 文件。我们将用它来定义路由逻辑来处理数据的端到端处理。
CRUD操作是基于模型完成的,这有助于维护发送和接收客户端的数据的结构(以及它们之间的关系)。因此,我们将添加一个todo模型,维护发送和接收数据的结构。
创建一个todos模块
// the todos module
package todos
导入包和模块
import (
// import modules
"go-fiber-todos/database"
"strconv"
// import packages
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
"gorm.io/gorm"
)
添加Todo结构
struct 是用来保存一个模型的集合的。在这种情况下,我们要设置和保持todos数据库的设置。我们主要对一个todo的 , 和完成的 感兴趣。ID Name status
Gorm正在帮助我们设置这个模型。通过Gorm,我们可以设置我们想要发展到数据库的数据结构。这有助于维护被发送和接收的数据的设计。
// Todo is a struct holding the todos settings.
type Todo struct {
gorm.Model
Id int `gorm:"primaryKey"`
Name string `json:"name"`
Completed bool `json:"completed"`
}
获取所有的todos
这里我们要创建一个GetAll() 方法。它将负责处理所有获取所有todos列表的请求。它以Fiber context为参数,帮助设置一个处理程序。一旦todos被获取,我们将以JSON格式返回。
// @ func GetAll -> function that fetches a single all todos (Get all todos)
// @param c *fiber.Ctx -- fiber context
func GetAll(c *fiber.Ctx) error {
db := database.DBConn
var todoss []Todo
db.Find(&todoss)
// If the database read is successful
return c.Status(fiber.StatusOK).JSON(todoss)
}
基于id获取一个单独的todo
我们正在创建一个GetOne() ,只获取一个todo。在这种情况下,当设置这个处理程序时,我们使用todoid 作为参数。这意味着我们将把处理程序的端点创建为一个带有命名参数的路由。
Go Fiber将把这个参数映射到请求单个todo的端点。因此,例如,当设置路由处理GetOne() ,将解析一个todo特有的id ,然后返回与该id/参数相匹配的获取的数据。
在这种情况下,我们需要处理服务器发回的内容。如果id 匹配任何现有的todo,该todo将被返回到Fiber context并输出JSON格式。如果id 不存在,用户将得到一个错误todo not found 。
// @ func GetOne -> function that fetches a single todo (Get single todo)
// @param c *fiber.Ctx -- fiber context
func GetOne(ctx *fiber.Ctx) error {
paramsId := ctx.Params("id")
id, err := strconv.Atoi(paramsId)
if err != nil {
ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "cannot parse id",
})
return err
}
db := database.DBConn
var todo Todo
db.Find(&todo, id)
// If the database read is successful
if int(todo.Id) == id{
return ctx.Status(fiber.StatusOK).JSON(todo)
}
// If the database fails to read the id parameter
return ctx.Status(fiber.StatusNotFound).JSON(fiber.Map{
"error": "todo not found",
})
}
创建一个新的todo
下面代码中的AddTodo() 函数创建一个新的todo,并将其保存到数据库中。这里我们只添加Name 。Completed 状态被默认设置为false 。
这就是uuid 开始发挥作用的地方。我们使用UUID实例来生成和检查我们的数据结构,然后再将其插入到数据库中。它生成了一个不可改变的通用唯一标识符(UUID)随机数。从而确保每个id对一个todo是唯一的。
// @func AddTodo -> function that stores a new data (Create new todo)
// @param c *fiber.Ctx -- fiber context
func AddTodo(ctx *fiber.Ctx) error {
db := database.DBConn
type request struct {
Name string `json:"name"`
}
// Parse POST data
var body request
err := ctx.BodyParser(&body)
if err != nil {
ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "cannot parse json",
})
return err
}
// Get the json struct that is required to send
id := uuid.New()
todo := Todo{
Id: int(id.ID()),
Name: body.Name,
Completed: false,
}
// Insert to DB
db.Create(&todo)
return ctx.Status(fiber.StatusOK).JSON(todo)
}
根据id删除一个todo
在下面的代码中,DeleteTodo() 将删除一个现有的todo。我们必须指定一个id作为Delete hanker和endpoint的参数。这定义了我们要删除的单一和独特的todo。
这里的id必须是一个现有的todo。否则,我们将返回一个错误。我们首先需要获取该todo,然后向数据库发送一个删除请求,删除与该todo相关的id。
// @func DeleteTodo -> a function that deletes the data (Delete todo)
// @param c *fiber.Ctx -- fiber context
func DeleteTodo(ctx *fiber.Ctx) error {
db := database.DBConn
paramsId := ctx.Params("id")
id, err := strconv.Atoi(paramsId)
if err != nil {
return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "cannot parse id",
})
}
var todo Todo
db.First(&todo, id)
if int(todo.Id) != id {
return ctx.Status(fiber.StatusNotFound).JSON(fiber.Map{
"error": "todo not found",
})
}
db.Delete(&todo)
return ctx.Status(fiber.StatusOK).JSON(fiber.Map{
"status": "todo deleted successfully",
})
}
更新一个现有的todo
在下面的代码中,UpdateTodo() 将更新一个现有todo的值。我们需要首先通过指定参数id来获取一个单独的todo。
在这里,我们可以改变todo的名称和todo的completed 值。完成的值会将todo更新为完整的,并有一个true 。
// @func UpdateTodo -> a function that ulters a todo data (Update todo)
// @param c *fiber.Ctx -- fiber context
func UpdateTodo(ctx *fiber.Ctx) error {
db := database.DBConn
type request struct {
Name *string `json:"name"`
Completed *bool `json:"completed"`
}
paramsId := ctx.Params("id")
id, err := strconv.Atoi(paramsId)
if err != nil {
return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "cannot parse id",
})
}
var body request
err = ctx.BodyParser(&body)
if err != nil {
return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error" : "Cannot parse body",
})
}
var todo Todo
// Check if todo exist, if exist assign it value to todo
db.First(&todo, id)
// handling 404 error
if int(todo.Id) != id {
return ctx.Status(fiber.StatusNotFound).JSON(fiber.Map{
"error": "todo not found",
})
}
if body.Name != nil {
todo.Name = *body.Name
}
if body.Completed != nil {
todo.Completed = *body.Completed
}
db.Save(&todo)
return ctx.Status(fiber.StatusOK).JSON(todo)
}
将处理程序分配给相应的路由
我们已经添加了所有的函数和处理程序,并为每个CRUD操作定义了所有的逻辑。这些处理程序是使用端点访问的。这些是URLs ,发送请求以执行对数据库的操作或向用户提供特定数据。我们将在main.go 文件中实现这一点。
创建一个todos模块
// the main module
package main
导入包和模块
在这里,我们要导入我们想要使用的本地、本地和第三方的包和模块。
import (
// import packages
"fmt"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/logger"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
// import modules
"go-fiber-todos/database"
"go-fiber-todos/todos"
)
设置Fiber应用程序
为了执行Go Fiber,我们需要设置一个Fiber应用程序。这里我们要添加Group ,它为我们的处理程序定义了路由。Group 也用于设置路由的共同前缀。在这种情况下,每个路径将有一个/v1 前缀。
// App config => App denotes the Fiber application.
func setupV1(app *fiber.App) {
// Group is used for Routes with a common prefix to define a new sub-router with optional middleware.
v1 := app.Group("/v1")
//Each route will have /v1 prefix
setupTodosRoutes(v1)
}
设置应用程序的路由
使用纤维Group ,让我们来设置我们的路由。每个路由将执行我们在todo.go 中设置的一个处理程序。所以我们在这里,添加简单路由和带参数的路由。
一个简单的路由除了设置"/" 端点外,不需要任何额外的参数。然而,一个带参数的路由有额外的参数,你需要通过这些参数来执行一个给定的端点。在我们的例子中,所有带参数的路由都把:id 作为额外参数。
// Router defines all router handle interface includes app and group router
func setupTodosRoutes(grp fiber.Router) {
// Group is used for Routes with common prefix => Each route will have /todos prefix
todosRoutes := grp.Group("/todos")
// Route for Get all todos -> navigate to => http://127.0.0.1:3000/v1/todos/
todosRoutes.Get("/", todos.GetAll)
// Route for Get a todo -> navigate to => http://127.0.0.1:3000/v1/todos/<todo's id>
todosRoutes.Get("/:id", todos.GetOne)
// Route for Add a todo -> navigate to => http://127.0.0.1:3000/v1/todos/
todosRoutes.Post("/", todos.AddTodo)
// Route for Delete a todo -> navigate to => http://127.0.0.1:3000/v1/todos/<todo's id>
todosRoutes.Delete("/:id", todos.DeleteTodo)
// Route for Update a todo -> navigate to => http://127.0.0.1:3000/v1/todos/<todo's id>
todosRoutes.Patch("/:id", todos.UpdateTodo)
}
初始化数据库
我们设置了数据库,但我们没有初始化或创建SQLite数据库。这里我们需要创建一个todos.db 文件来保存我们的todo。
我们也在迁移设定的数据库结构。所有这些都将在我们建立应用程序时自动创建。
// Database Connect function
func initDatabase() {
// define error here to prevent overshadowing the global DB
var err error
// Create todos sqlite file & Config GORM config
// GORM performs single create, update, delete operations in transactions by default to ensure database data integrity
database.DBConn, err = gorm.Open(sqlite.Open("todos.db"), &gorm.Config{})
// Connect to database
if err != nil {
// Database was connected
panic("failed to connect database")
}
fmt.Println("Database successfully connected")
// AutoMigrate run auto migration for gorm model
database.DBConn.AutoMigrate(&todos.Todo{})
// Initialize Database connection
fmt.Println("Database Migrated")
}
定义应用程序的入口点
这将定义我们的应用程序入口点。
我们正在执行以下操作。
- 实例化一个新的Fiber App。
- 调用
initDatabase()方法。 - 调用
setupV1(app)方法。 - 为一个简单的路由设置一个中间件函数,返回纯文本。
- 设置一个记录器中间件。这个中间件将被用来记录HTTP动词。GET、POST、PUT,等等。对于每个路由,当每个HTTP动词被调用时。
- 添加一个服务器端口号。这将被用来在localhost上运行我们的服务器。
- 处理
panic错误。panic是Go内置的一个函数,它监视所有函数的执行。如果一个函数出现错误,正常的执行会立即停止。
// entry point to our program
func main() {
// call the New() method - used to instantiate a new Fiber App
app := fiber.New()
// call the initDatabase() method
initDatabase()
// call the setupV1(app) method
setupV1(app)
// Simple route => Middleware function
app.Get("/", func(c *fiber.Ctx) error {
// Returns plain text.
return c.SendString("Hello, World!")
// navigate to => http://127.0.0.1:3000
})
// sets up logger
// Use middlewares for each route
// This method will match all HTTP verbs: GET, POST, PUT etc Then create a log when every HTTP verb get invoked
app.Use(logger.New(logger.Config{ // add Logger middleware with config
Format: "[${ip}]:${port} ${status} - ${method} ${path}\n",
}))
// listen/Serve the new Fiber app on port 3000
err := app.Listen(":3000")
// handle panic errors => panic built-in function that stops the execution of a function and immediately normal execution of that function with an error
if err != nil {
panic(err)
}
}
todo应用程序已经设置好了,可以进行测试了。
测试
我们将使用air 命令来启动服务器,也就是说,在你的项目根目录下运行以下命令。
air
这将构建一个可执行文件并将其保存在temp 文件夹中。下面是命令的输出。

有了air ,观察和构建我们的服务器就变得很容易了。如果你改变了任何代码,服务器将重新加载、构建并重新运行。首先,air 将删除tmp 的可执行文件,重建应用程序,并保存新构建的可执行文件。

现在让我们测试一下我们的端点。同样,我们将使用Postman来测试这些端点。
打开你的Postman,向http://127.0.0.1:3000 发送一个GET请求,如下图所示。

现在让我们测试一下todo路由。
- 添加一个新的todo。
让我们先在数据库中添加一个todos的列表。下面是一个我想插入到SQLite数据库的todo列表样本。接下来,导航到Postman并执行一个post请求,如下图所示。

新的todo如下图所示。

如果你得到的东西与你添加的东西不同,你可能忘记了一两个步骤。重新审视你的AddTodo() 函数,或者检查一下你使用的JSON数据是否有良好的格式化。
另外,继续添加一些其他的todos。
- 取出todos。
现在让我们来获取添加的todos。这里将执行一个获取请求,如下图所示。


- 获取一个单独的todo。
要获取单个todo,你需要在你的URL中指定todo的id作为一个参数。
看看这个例子。

- 更新一个todo。
一旦一个todo被添加,我们可以执行更新操作来改变该todo的值。在这里,我们必须指定我们想要更新的todo的ID。然后添加你想替换todo的数据。

一旦你发送一个PATCH请求,分配给该todo的id的值就会更新。
下面是一个例子。

- 删除一个todo。
现在让我们执行最后一项操作,删除一个现有的todo。让我们指定你要删除的todo的id,如下图所示。

如果你试图向被删除的todo发送一个GET请求,你应该得到一个错误提示" error": "todo not found"

总结
Golang是一种神奇的语言。你可以创建几乎任何你在其他语言中可以创建的应用程序。此外,Go可以处理大量的应用程序。因此,它可以建立各种级别的应用程序,同时由于其利用多核处理的能力而确保最小化。