深圳幻海软件技术有限公司 欢迎您!

项目实战:使用 Fiber + Gorm 构建 Rest API

2023-02-28

大家好,我是程序员幽鬼。Fiber作为一个新的Go框架,似乎受追捧程度很高,Star数飙升很快。不知道这是不是表明,不少JS/Node爱好者开始尝试学习Go了,对Go是好事。今天这篇文章介绍如何使用Fiber+Gorm构建RESTAPI。1概览在这篇文章中,我们将使用Fiber[1]框架,它使用起来

大家好,我是程序员幽鬼。

Fiber 作为一个新的 Go 框架,似乎受追捧程度很高,Star 数飙升很快。不知道这是不是表明,不少 JS/Node 爱好者开始尝试学习 Go 了,对 Go 是好事。

今天这篇文章介绍如何使用 Fiber + Gorm 构建 REST API。

1 概览

在这篇文章中,我们将使用 Fiber[1] 框架,它使用起来非常简单,有一个很好的抽象层,并且包含我们创建 API 所需的一切。

关于与数据库的交互,我决定使用 ORM 来使整个过程更简单、更直观,因此我决定使用Gorm[2],在我看来,Gorm[3] 是 Go 世界中最受欢迎的 ORM,并且特性很多。

2 准备工作

本文基于 Go1.17.5。

在本地创建一个目录 fibergorm,然后进入该目录执行如下命令:

$ go mod init github.com/programmerug/fibergorm 
go: creating new go.mod: module github.com/programmerug/fibergorm 
  • 1.
  • 2.

接着执行如下命令,安装我们需要的依赖:(这个先不执行,之后通过 go mod tidy 安装)

go get github.com/gofiber/fiber/v2 
go get gorm.io/gorm 
  • 1.
  • 2.

为了方便,本教程中,我们使用 SQLite,因为使用了 Gorm,所以哪种关系型数据库对核心代码没有什么影响。

3 开始编码

本文以人类的朋友——狗为例。

先从定义实体开始,它总共有四个属性:

  • Name - 狗的名字
  • Age - 狗的年龄
  • Breed - 狗的种族(类型)
  • IsGoodBoy - 狗是否是个好孩子

类似以下内容:

// 文件名:entities/dog.go 
package entities 
 
import "gorm.io/gorm" 
 
type Dog struct { 
    gorm.Model 
    Name      string `json:"name"
    Age       int    `json:"age"
    Breed     string `json:"breed"
    IsGoodBoy bool   `json:"is_good_boy" gorm:"default:true"

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

注意其中的内嵌类型 gorm.Model,它只是定义了一些通用的字段。

type Model struct { 
    ID        uint `gorm:"primarykey"
    CreatedAt time.Time 
    UpdatedAt time.Time 
    DeletedAt DeletedAt `gorm:"index"

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

因此,我们完全可以自己选择是否要嵌入 gorm.Model。

接着,我们配置与数据库的连接。一般我喜欢创建一个名为 Connect() 的函数,它负责初始化连接,此外还负责在我们的数据库中执行迁移(migration),即生成表结构:

// 文件名:config/database.go 
package config 
 
import ( 
    "github.com/programmerug/fibergorm/entities" 
    "gorm.io/driver/sqlite" 
    "gorm.io/gorm" 

 
var Database *gorm.DB 
 
func Connect() error { 
    var err error 
 
    Database, err = gorm.Open(sqlite.Open("fibergorm.db"), &gorm.Config{}) 
 
    if err != nil { 
        panic(err) 
    } 
 
    Database.AutoMigrate(&entities.Dog{}) 
 
    return nil 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • fibergorm.db 是最后生成的数据库文件
  • 在程序启动时,需要调用 Connect 函数

现在已经定义了实体并配置了到数据库的连接,我们可以开始处理我们的处理程序。我们的每个处理程序都将对应来自 API 的一个路由,每个处理程序只负责执行一个操作。首先让我们获取数据库表中的所有记录。

// 文件名:handlers/dog.go 
package handlers 
 
import ( 
    "github.com/gofiber/fiber/v2" 
    "github.com/programmerug/fibergorm/config" 
    "github.com/programmerug/fibergorm/entities" 

 
func GetDogs(c *fiber.Ctx) error { 
    var dogs []entities.Dog 
 
    config.Database.Find(&dogs) 
    return c.Status(200).JSON(dogs) 

 
// ... 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

现在根据将在请求参数中发送的 id 参数获取一条记录。

// 文件名:handlers/dog.go 
package handlers 
 
// ... 
 
func GetDog(c *fiber.Ctx) error { 
    id := c.Params("id"
    var dog entities.Dog 
 
    result := config.Database.Find(&dog, id) 
 
    if result.RowsAffected == 0 { 
        return c.SendStatus(404) 
    } 
 
    return c.Status(200).JSON(&dog) 

 
// ... 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

现在我们可以得到所有的记录和根据 id 获取一条记录。但缺乏在数据库表中插入新记录的功能。

// 文件名:handlers/dog.go 
package handlers 
 
// ... 
 
func AddDog(c *fiber.Ctx) error { 
    dog := new(entities.Dog) 
 
    if err := c.BodyParser(dog); err != nil { 
        return c.Status(503).SendString(err.Error()) 
    } 
 
    config.Database.Create(&dog) 
    return c.Status(201).JSON(dog) 

 
// ... 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

我们还需要添加更新数据库中现有记录的功能。与我们已经实现的类似,使用id参数来更新特定记录。

// 文件名:handlers/dog.go 
package handlers 
 
// ... 
 
func UpdateDog(c *fiber.Ctx) error { 
    dog := new(entities.Dog) 
    id := c.Params("id"
 
    if err := c.BodyParser(dog); err != nil { 
        return c.Status(503).SendString(err.Error()) 
    } 
 
    config.Database.Where("id = ?", id).Updates(&dog) 
    return c.Status(200).JSON(dog) 

 
// ... 
最后,我们需要删除特定记录,再次使 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

最后,我们需要删除特定记录,再次使用 id 参数从我们的数据库中删除特定记录。

// 文件名:handlers/dog.go 
package handlers 
 
// ... 
 
func RemoveDog(c *fiber.Ctx) error { 
    id := c.Params("id"
    var dog entities.Dog 
 
    result := config.Database.Delete(&dog, id) 
 
    if result.RowsAffected == 0 { 
        return c.SendStatus(404) 
    } 
 
    return c.SendStatus(200) 

 
// ... 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

现在只需要创建我们的 main 文件,该文件将负责初始化与数据库的连接以及我们的 API 路由将在何处定义,并且将处理程序与它们进行关联绑定。

// 文件名:main.go 
package main 
 
import ( 
    "log" 
 
    "github.com/gofiber/fiber/v2" 
    "github.com/programmerug/fibergorm/config" 
    "github.com/programmerug/fibergorm/handlers" 

 
func main() { 
    app := fiber.New() 
 
    config.Connect() 
 
    app.Get("/dogs", handlers.GetDogs) 
    app.Get("/dogs/:id", handlers.GetDog) 
    app.Post("/dogs", handlers.AddDog) 
    app.Put("/dogs/:id", handlers.UpdateDog) 
    app.Delete("/dogs/:id", handlers.RemoveDog) 
 
    log.Fatal(app.Listen(":3000")) 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.

至此,我们完成了一个简单应用的 CRUD。涉及到 fiber 和 gorm 的 API 你应该查阅相关文档进一步了解。

最终,项目目录如下:

├── config 
│   └── database.go 
├── entities 
│   └── dog.go 
├── fibergorm.db 
├── go.mod 
├── go.sum 
├── handlers 
│   └── dog.go 
└── main.go 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

执行 go run main.go:

$ go run main.go 
 
 ┌───────────────────────────────────────────────────┐ 
 │                   Fiber v2.24.0                   │ 
 │               http://127.0.0.1:3000               │ 
 │       (bound on host 0.0.0.0 and port 3000)       │ 
 │                                                   │ 
 │ Handlers ............. 7  Processes ........... 1 │ 
 │ Prefork ....... Disabled  PID ............. 89910 │ 
 └───────────────────────────────────────────────────┘ 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

借助 postman、curl 之类的工具验证接口的正确性。

因为 fiber Ctx 的 BodyParser 能够解析 Context-Type 值是:application/json, application/xml, application/x-www-form-urlencoded, multipart/form-data 等的数据,所以,验证 AddDog 的可以采用你喜欢的 Content-Type。

curl --location --request POST 'http://127.0.0.1:3000/dogs' \ 
--header 'Content-Type: application/json' \ 
--data '{name:"旺财",age:3,breed:"狼狗",is_good_boy:true}' 
  • 1.
  • 2.
  • 3.

可以通过下载 https://sqlitebrowser.org/dl/ 这个 SQLite 工具查看数据是否保存成功。

细心的读者可能会发现,生成的数据表字段顺序是根据 Dog 中的字段定义顺序确定的。有强迫症的人可能接受不了,因此实际中你可以不嵌入 gorm.Model,而是自己定义相关字段。

总结

本文实现了 CRUD 的功能,希望大家实际动手,这样才能够真正掌握。

本文参考 https://dev.to/franciscomendes10866/how-to-build-rest-api-using-go-fiber-and-gorm-orm-2jbe。

本文完整代码:https://github.com/programmerug/fibergorm。

参考资料

[1]Fiber: https://gofiber.io/

[2]Gorm: https://gorm.io/

[3]Gorm: https://gorm.io/