使用 Fiber 在 Go 中构建基本的 REST API

Fiber是一个新的基于 Go 的 Web 框架,它已经爆发并引起了编程社区的极大兴趣。该框架的存储库一直位于 Go 编程语言的 GitHub 趋势页面上,因此,我想我会打开旧的 VS Code 并尝试构建一个简单的 REST API。

因此,在本教程中,我们将介绍如何使用这个新的 Fiber 框架开始在 Go 中构建自己的 REST API 系统!

在本教程结束时,我们将介绍:

  • 项目设置
  • 为图书管理系统构建 Simle CRUD REST API
  • 使用附加包将项目分解为更可扩展的格式。

让我们潜入水中!

为什么是Fiber?

如果您来自另一种语言并尝试开发 Go 应用程序,那么 Fiber 是一个非常容易上手的框架。它为以前使用 Express.js 构建系统的 Node.js 开发人员提供了一种熟悉的感觉。它还建立在它之上,Fasthttp它是为 Go 构建的令人难以置信的高性能和最小的 HTTP 引擎。

如果我们看一下项目中的快速启动代码,README.md我们可以看到我们可以多么快速和简单地获得一个基于简单HTTP GET的端点返回 a Hello, World!

main.go
package main

import "github.com/gofiber/fiber"

func main() {
  app := fiber.New()

  app.Get("/", func(c *fiber.Ctx) {
    c.Send("Hello, World!")
  })

  app.Listen(3000)
}

然后我们可以运行它并启动我们的服务器http://localhost:3000,首先使用初始化我们的项目go mod init,然后运行go run main.go它将Fiber在启动服务器之前下载所有的依赖项:

$ go mod init github.com/tutorialedge/go-fiber-tutorial
$ go run main.go
Fiber v1.9.1 listening on :3000

太棒了,我们现在有了可以开始构建更复杂系统的基础!😎

介绍

让我们从修改快速启动代码并使其更具可扩展性开始:

main.go
package main

import (
	"github.com/gofiber/fiber"
)

func helloWorld(c *fiber.Ctx) {
	c.Send("Hello, World!")
}

func setupRoutes(app *fiber.App) {
	app.Get("/", helloWorld)
}

func main() {
	app := fiber.New()

	setupRoutes(app)
	app.Listen(3000)
}

让我们分解一下我们在这里做了什么。

  • 我们创建了一个名为的新函数setupRoutes,我们将指针传递给我们的app. 在这个setupRoutes函数中,我们将端点映射到命名函数。此更改允许我们将路由逻辑从应用程序初始化逻辑中移出,如果我们要编写更复杂的应用程序,这很重要。
  • 我们已经创建了已将端点helloWorld映射到的命名函数。/这种变化允许我们编写更复杂的端点函数。

构建我们的 REST API 端点

所以,有了这些新的变化,现在让我们看看扩展我们的应用程序的功能并创建一些额外的端点,我们可以从中服务请求。我们将构建一个图书管理系统,该系统将在大流行锁定期间存储我们一直在阅读的图书的内存存储!

我们将要创建以下端点:

  • /api/v1/book– 一个HTTP GET端点,它将返回您在锁定期间阅读的所有书籍。
  • /api/v1/book/:id– 一个HTTP GET端点,它接受书籍 ID 的路径参数并仅返回一本单独的书籍
  • /api/v1/book– 一个HTTP POST端点,允许我们将新书添加到列表中
  • /api/v1/book/:id– 一个HTTP DELETE端点可以让我们从列表中删除一本书,以防我们错误地添加任何书籍?

挑战– 添加HTTP PUT用于更新列表中书籍的端点。

让我们看看我们现在如何开始构建它。

书包

文件中没有足够的介绍性教程main.go,我过去一直对此感到内疚。因此,让我们打破这个循环并建立一些可以轻松扩展的坚实基础,如果您希望使用本教程中的代码构建更复杂的应用程序。

我们将从在 Go 项目中创建一个新包开始。这将包含我们书籍端点的所有逻辑:

$ mkdir -p book
$ cd book
$ touch book.go

在这个新创建book.go的文件中,让我们开始为我们将映射到上述端点的函数定义存根:

书/book.go
package book

import (
	"github.com/gofiber/fiber"
)

func GetBooks(c *fiber.Ctx) {
	c.Send("All Books")
}

func GetBook(c *fiber.Ctx) {
	c.Send("Single Book")
}

func NewBook(c *fiber.Ctx) {
	c.Send("New Book")
}

func DeleteBook(c *fiber.Ctx) {
	c.Send("Delete Book")
}

有了这个,我们就可以返回main.go文件,在我们的setupRoutes函数中,我们可以将端点映射到这些新函数,如下所示:

main.go
package main

import (
	"github.com/elliotforbes/go-fiber-tutorial/book"
	"github.com/gofiber/fiber"
)

func helloWorld(c *fiber.Ctx) {
	c.Send("Hello, World!")
}

func setupRoutes(app *fiber.App) {
	app.Get("/", helloWorld)

	app.Get("/api/v1/book", book.GetBooks)
	app.Get("/api/v1/book/:id", book.GetBook)
	app.Post("/api/v1/book", book.NewBook)
	app.Delete("/api/v1/book/:id", book.DeleteBook)
}

func main() {
	app := fiber.New()

	setupRoutes(app)
	app.Listen(3000)
}

很酷!我们现在已经导入了我们的新book包并将我们想要的端点映射到这 4 个新函数。

让我们尝试使用一些curl命令来访问这些端点,看看它们是否以我们期望的方式响应:

$ curl http://localhost:3000/api/v1/book
All Books

$ curl http://localhost:3000/api/v1/book/1
Single Book

$ curl -X POST http://localhost:3000/api/v1/book
New Book

$ curl -X DELETE http://localhost:3000/api/v1/book/1
Delete Book

太棒了,所有 4 个端点都为各自的 HTTP 请求返回了正确的响应!

添加数据库

现在我们已经定义了各自的端点并按预期工作,让我们看一下设置一个简单的数据库,我们将使用它来与之交互,gorm从而简化我们与数据库的对话!

在项目目录的根目录中,运行以下命令以创建一个名为的新文件夹database/和一个名为的新文件database.go

$ mkdir -p database
$ cd database
$ touch database.go

在这个新的 database.go 文件中,我们将要定义一个全局DBConn变量,它是一个指向数据库连接的指针,我们的端点将使用它与本地 sqlite 数据库进行交互:

数据库/database.go
package database

import (
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/sqlite"
)

var (
	DBConn *gorm.DB
)

有了这个,我们将要更新我们的文件,通过创建一个新函数main.go来打开到这个 sqlite 数据库的连接。initDatabase()

main.go

package main

import (
	"fmt"
	"github.com/elliotforbes/go-fiber-tutorial/database"
	"github.com/gofiber/fiber"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/sqlite"
)

func setupRoutes(app *fiber.App) {
	app.Get("/api/v1/book", book.GetBooks)
	app.Get("/api/v1/book/:id", book.GetBook)
	app.Post("/api/v1/book", book.NewBook)
	app.Delete("/api/v1/book/:id", book.DeleteBook)
}

func initDatabase() {
	var err error
	database.DBConn, err = gorm.Open("sqlite3", "books.db")
	if err != nil {
		panic("failed to connect database")
	}
	fmt.Println("Connection Opened to Database")
}

func main() {
	app := fiber.New()
	initDatabase()

	setupRoutes(app)
	app.Listen(3000)

	defer database.DBConn.Close()
}

接下来,我们必须更新我们的book/book.go代码,以便我们定义一个struct用于创建数据库表的 Book。

package book

import (
	"fmt"

	"github.com/elliotforbes/go-fiber-tutorial/database"
	"github.com/gofiber/fiber"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/sqlite"
)


type Book struct {
	gorm.Model
	Title  string `json:"name"`
	Author string `json:"author"`
	Rating int    `json:"rating"`
}

更新我们的端点

接下来,我们需要更新映射到每个端点的函数。让我们从更新GetBooks以归还所有书籍开始:

func GetBooks(c *fiber.Ctx) {
	db := database.DBConn
	var books []Book
	db.Find(&books)
	c.JSON(books)
}

使用c.JSONFiber 提供给我们的方法,我们可以快速轻松地将 books 数组序列化为 JSON 字符串并在响应中返回!

接下来让我们更新我们的单书端点:

func GetBook(c *fiber.Ctx) {
	id := c.Params("id")
	db := database.DBConn
	var book Book
	db.Find(&book, id)
	c.JSON(book)
}

在这里,我们使用该c.Params("id")函数来检索表示ID我们要检索的书的路径参数。我们可以再次使用该c.JSON函数返回这本书。

注意– 我没有费心为这个特定的端点添加错误处理,它总是假设这本书存在。我将把它作为一个挑战留给读者来处理这个案例。

添加和删​​除书籍

到目前为止,我们刚刚处理了从数据库中检索书籍,让我们看看如何通过更新NewBookDeleteBook函数开始添加和删除书籍。

NewBook函数中,让我们对我们现在要填充的书进行硬编码,以便我们可以增量测试我们的 API。这将调用db.Create以便为我们将新书推送到数据库中,然后我们将返回JSON该书的:

func NewBook(c *fiber.Ctx) {
	db := database.DBConn
	var book Book
	book.Title = "1984"
	book.Author = "George Orwell"
	book.Rating = 5
	db.Create(&book)
	c.JSON(book)
}

完美,现在终于让我们更新DeleteBook功能了。在这里,我们将实际执行一些错误处理并检查该书是否首先存在于数据库中,然后再尝试删除该书并返回一条确认删除的简单消息:

func DeleteBook(c *fiber.Ctx) {
	id := c.Params("id")
	db := database.DBConn

	var book Book
	db.First(&book, id)
	if book.Title == "" {
        c.Status(500).Send("No Book Found with ID")
        return
	}
	db.Delete(&book)
	c.Send("Book Successfully deleted")
}

迁移我们的数据库

幸运的是,Gorm 为我们处理了表的创建和任何更新,因此设置所有这些的复杂性是最小的。我们需要添加调用以AutoMigrate传入我们想要基于以下内容生成表的结构:

main.go
func initDatabase() {
	var err error
	database.DBConn, err = gorm.Open("sqlite3", "books.db")
	if err != nil {
		panic("failed to connect database")
	}
	fmt.Println("Connection Opened to Database")
	database.DBConn.AutoMigrate(&book.Book{})
	fmt.Println("Database Migrated")
}

当我们下次启动 API 时,它会在我们的 sqlite 数据库中自动为我们生成表。

测试我们的端点:

现在我们已经定义了端点并与数据库通信,下一步是手动测试它们以验证它们是否按预期工作:

$ curl http://localhost:3000/api/v1/book
[{"ID":3,"CreatedAt":"2020-04-24T09:20:37.622829+01:00","UpdatedAt":"2020-04-24T09:20:37.622829+01:00","DeletedAt":null,"name":"1984","author":"George Orwell","rating":5},{"ID":4,"CreatedAt":"2020-04-24T09:29:47.573672+01:00","UpdatedAt":"2020-04-24T09:29:47.573672+01:00","DeletedAt":null,"name":"1984","author":"George Orwell","rating":5}]

$ curl http://localhost:3000/api/v1/book/1
{"ID":3,"CreatedAt":"2020-04-24T09:20:37.622829+01:00","UpdatedAt":"2020-04-24T09:20:37.622829+01:00","DeletedAt":null,"name":"1984","author":"George Orwell","rating":5}

$ curl -X POST http://localhost:3000/api/v1/book
{"ID":5,"CreatedAt":"2020-04-24T09:49:16.405426+01:00","UpdatedAt":"2020-04-24T09:49:16.405426+01:00","DeletedAt":null,"name":"1984","author":"George Orwell","rating":5}

$ curl -X DELETE http://localhost:3000/api/v1/book/1
Book Successfully Deleted

所有这些都按照我们的预期工作了!我们现在有一个主要功能的 REST API,我们可以与之交互并在其上抛出一个前端!

读取 JSON 请求数据

我想在本教程中介绍的最后一件事是读取传入请求的正文并将其解析为 a book struct,以便我们可以将自定义数据填充到我们的数据库中。

值得庆幸的是,该fiber框架具有一个非常方便的BodyParser方法,可以读取请求正文,然后为我们填充一个结构,如下所示:

func NewBook(c *fiber.Ctx) {
	db := database.DBConn
	book := new(Book)
	if err := c.BodyParser(book); err != nil {
		c.Status(503).Send(err)
		return
	}
	db.Create(&book)
	c.JSON(book)
}

有了这个新的变化,让我们重新运行我们的 API 并 使用 curl 命令发送一个 HTTP POST 请求,我们将在一本新书中传递该命令:

$ curl -X POST -H "Content-Type: application/json" --data "{\"title\": \"Angels and Demons\", \"author\": \"Dan Brown\", \"rating\": 4}" http://localhost:3000/api/v1/book
{"ID":6,"CreatedAt":"2020-04-24T10:50:52.658811+01:00","UpdatedAt":"2020-04-24T10:50:52.658811+01:00","DeletedAt":null,"title":"Angels and Demons","author":"Dan Brown","rating":4}

一切都按预期工作!通过我们提供的信息,我们可以看到新书正在为我们添加到数据库中!

结论

🔥 太棒了,所以在本教程中,我们设法使用 Fiber 框架为 Go 中的图书管理系统构建了一个非常简单的 REST API!🔥

发表评论

邮箱地址不会被公开。 必填项已用*标注