使用 Go 和 Fiber 实现 Web 服务

Web 开发的原则对于所有 Web 框架都是相似的。让我们借助 Go 编程语言和 Fiber 框架学习 Web 开发的基础知识,编写最简单的 Web 服务。

框架存在的原因是什么?

每个流行的 Web 服务都基于客户端和服务器之间的交换。不断重复的请求处理和响应返回包括许多与大多数站点相似的元素。

我们不想为每个新网站开发类似的工具。我们希望为类似的逻辑创建可重用的工具,而不是每次都发明新的东西。框架就是为这些目的而设计的。它们包括标准 Web 开发问题的机制。多亏了框架,我们无法更快地解决业务任务,几乎可以跳过低级技术问题。

请求和响应

大多数现代 Web 框架都基于响应请求范式,它不依赖于编程语言和实现特性。

让我们用 Fiber 框架实现最简单的 Web 服务器,以了解它的外观。Fiber 简约但速度快,并且包含实施所需的所有必要工具。我们可以在网站上找到安装说明。

Hello, World!

让我们用 Fiber 编写最简单的应用程序。传统上,它将是一个返回Hello, World!的 Web 服务器。创建server.go具有以下内容的文件:

package main 

import "github.com/gofiber/fiber/v2"

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

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

	app.Listen(":3000")
}

使用终端中的命令运行应用程序

go run server.go

在浏览器中,输入http://localhost:3000并看到文本Hello, World!

让我们拆开应用程序,看看服务器如何理解要做什么,同时,让我们了解一个关键概念:路由。

  1. 在这段代码中,我们创建了一个名为main. 每个 Go 包都有一个main函数,我们的代码也不例外。
  2. 导入所需的 Fiber 框架模块。
  3. main函数的开头,我们创建了一个新的 Web 服务器实例。它将服务请求并完成工作。
  4. 下一步是处理路由/.Internet 请求具有地址或 URL。如果我们已经在服务器内部发送我们的处理请求,我们就不需要端口或域。我们只需要来自源地址的路径和请求中使用的方法。
  5. 稍后我们将更详细地查看其他选项,但现在,让我们假设服务器将使用最短路径/SendString处理 GET 请求,并使用context 方法发送一个字符串作为响应。
  6. 带有指向上下文结构的指针的参数被传递给处理函数。尽管设计和提示是特定于我们的框架的,但绑定到请求的上下文的概念是通用的。请求的上下文包含处理当前请求的必要信息和方法。
  7. 响应和查询对象的组件 – 带有元信息的标头
  8. 在我们main函数的最后一行,我们指定将处理哪个服务器的端口。这里是 3000,但任何端口都可以。您可以在不同端口上运行同一服务器的多个实例。

我们已经在/处编写了请求的处理,但是如何进一步处理其余的请求。毕竟,我们希望我们的服务器能够做其他事情。

更复杂的处理程序

到达服务器的每个请求都有一个地址。对于我们的服务器,当它在我们的机器上本地运行时,地址如下所示:http://localhost:3000/resource。浏览器已经知道我们的服务器在localhost:3000运行,所以我们不处理内部的那部分,但我们处理资源部分。如果我们从浏览器的地址栏发送请求 – 使用 GET 方法,所以在我们的框架内,我们重复我们已经拥有的地址处理程序/ 。它看起来像这样:

app.Get("/resource", func(c *fiber.Ctx) error {
	return c.SendString("Desired resource")
})

重新启动服务器,通过在浏览器中输入链接,我们得到结果 – 字符串Desired resource。

从示例中可以看出,函数的第一个参数带有 HTTP 请求方法的名称,负责处理 Fiber 中的特定路径。由第二个参数指定的处理函数负责对特定请求返回的结果。在 Web 服务器中匹配路径和处理程序称为路由,这是每个 Web 框架中的强制性步骤。

动态地址

地址可以是动态的并包含以下参数:用户 ID 或电影名称。Fiber 内部还有一个现成的功能。让我们使用以下参数实现一个请求处理程序http://localhost:3000/resource/resource_id

app.Get("/resource/:id", func(c *fiber.Ctx) error {
    return c.SendString("Desired resource = " + c.Params("id"))
})

参数名称前面的冒号将允许我们在请求处理程序中获取此值,我们通过将资源 ID 添加到响应中来演示。可以有多个参数,允许您实现复杂的路由。

这是带有两个参数http://localhost:3000/user/8427/book/HarryPotterandtheChamberofSecrets的路由和请求处理程序的样子:

app.Get("/user/:id/book/:title", func(c *fiber.Ctx) error {
    return c.SendString("Desired book is " + c.Params("title") + " from user " + c.Params("id"))
})

查询参数

除了直接在查询路径中包含参数之外,变量在查询参数中被替换。你可能见过像http://localhost:3000/book_cover?size=140×200&filename=cover.png 这样的 URL,URL 中的参数主要用于指定实体 ID 或名称,而查询参数用于传递任何信息。这是一种行之有效的做法,但没有什么能阻止您将任何内容作为 URL 参数传递。

当然,有长度限制——许多 Web 服务不允许使用很长的 URL。如果您需要大量数据,您应该使用 POST 请求并传递参数,而不是在 URL 中,而是在请求正文中。下面让我们看看查询参数处理的代码。上下文的Query函数用于从查询中获取参数。

app.Get("/book_cover", func(c *fiber.Ctx) error {
    return c.SendString("Desired book cover has size " + c.Query("size") + " with filename " + c.Query("filename"))
})

我们重新保存代码,调用我们的 URL 后,在响应中从请求中获取参数。必须在处理程序中获取参数并确保它们具有正确的格式和值。此步骤称为验证。

请求结构

在处理程序中,访问请求结构并查看必要的信息。Fiber 使用 request 方法隐藏了工作,并在我们方面毫不费力地配置了路由。可以直接获取方法如下:

app.Get("/", func(c *fiber.Ctx) error {
  c.Request().Header.Method()
  // => []byte("GET")
})

如我们所见,上下文方法Request将返回所有必需的信息。

响应结构

如果上下文中有请求对象,那么显然也有响应对象。它通常也需要访问。使用场景多种多样:返回返回的响应类型,存储key进行哈希,添加授权信息。此数据被写入响应标头。但是,您不应该在标题中返回文件,并且也有大小限制。

Fiber 包含隐藏原始响应的有用方法,但如果您愿意,可以使用响应对象,如以下代码片段所示:

// GET http://localhost:3000/custom_header
app.Get("/custom_header", func(c *fiber.Ctx) error {
    c.Response().Header.Set("X-My-Header", "my-header-value")
    return c.SendString("Hello, World!")
})

如果您在浏览器中查询 URL,您会得到标准响应Hello, World! 您可以在开发人员控制台中或使用 cUrl 或 Postman 等工具查看标题。

结论

今天,我们探讨了开发 Web 应用程序所涉及的几个概念。也就是说,值得再次记住 – 无论编程语言和框架如何,您都必须使用 HTTP 请求。

您将选择是将参数直接添加到 URL 路径还是将它们添加为查询参数。发出 GET 或 POST 请求以及从标题中添加或删除的内容。如果你想使用 Go 编程语言和 Fiber 框架,今天课程中的现成示例会有所帮助。

有用的链接

Golang官方网站

Fiber官网

什么是网址?

完整列表

当前文章中的所有应用程序代码附在下面:

package main

import "github.com/gofiber/fiber/v2"

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

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

	app.Get("/resource", func(c *fiber.Ctx) error {
		return c.SendString("Desired resource")
	})

	app.Get("/resource/:id", func(c *fiber.Ctx) error {
		return c.SendString("Desired resource = " + c.Params("id"))
	})

	app.Get("/user/:id/book/:title", func(c *fiber.Ctx) error {
		return c.SendString("Desired book is " + c.Params("title") + " from user " + c.Params("id"))
	})

	app.Get("/bookfile", func(c *fiber.Ctx) error {
		return c.SendString("Desired bookfile has size " + c.Query("size") + " with filename " + c.Query("filename"))
	})

	app.Get("/custom_header", func(c *fiber.Ctx) error {
		c.Response().Header.Set("X-My-Header", "my-header-value")
		return c.SendString("Hello, World!")
	})

	app.Listen(":3000")
}