首页
Preview

Go教程系列第一集:使用Gin构建REST API

欢迎来到我们的Go教程系列,我们将介绍实用的概念和如何编写一个Go Web服务,涵盖了REST API、数据库、授权和部署等方面。

在本篇文章中,我们将重点关注使用Gin框架(https://github.com/gin-gonic/gin)创建API的主题。

本教程假设你具有一些基本的Go编程知识(例如语法和基本概念)。如果你不知道Go是什么或如何编写基本的Go编程,请先从这里开始学习(https://go.dev/tour),或者你可以从Skooldio的课程中学习(https://www.skooldio.com/courses/go-the-fundamentals)。

先决条件

  1. 安装Go(了解如何安装Go,请参阅此处https://go.dev/doc/install

项目设置

  1. 创建项目目录
mkdir go-rest-api
  1. 初始化Go模块
go mod init example/go-rest-api
  1. 创建 main.go 文件并编写基本的Hello World代码
package main

import (
	"fmt"
)

func main() {
	fmt.Println("Hello World!")
}
  1. 在命令行中运行代码
go run main.go
  1. 你应该在命令行中看到输出
Hello World!

使用Gin创建基本API

  1. 从实现基本API开始。
package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.New()
	r.GET("/", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "Hello World!",
		})
	})
	r.Run()
}

首先,我们使用 gin.New() 创建路由器,然后使用 r.GET 方法注册路由 / 并指定我们的请求处理函数,最后使用 r.Run() 运行路由器。

在我们的处理函数中,我们使用 c.JSON 方法将响应写回客户端,以返回我们的JSON {"message": "Hello World!"}

  1. 使用以下命令安装依赖项。
go get
go mod tidy
  1. 运行代码以启动API服务器。
go run main.go
  1. 通过在浏览器中输入URL http://localhost:8080 来测试API,你应该会看到结果。

实现REST API

1. 创建数据结构和模拟数据

添加 Book Struct 和一个用于存储模拟API数据的 Slice

type Book struct {
	ID     string `json:"id"`
	Title  string `json:"title"`
	Author string `json:"author"`
}

var books = []Book{
	{ID: "1", Title: "Harry Potter", Author: "J. K. Rowling"},
	{ID: "2", Title: "The Lord of the Rings", Author: "J. R. R. Tolkien"},
	{ID: "3", Title: "The Wizard of Oz", Author: "L. Frank Baum"},
}

请注意,在 Book Struct 中,每个字段都有一个标记 json:"xxx" 遵循字段声明。这是为了JSON序列化API,使服务知道JSON中的哪个字段与Struct中的哪个字段相对应。

2. 实现GET端点以列出所有图书。

实现如下所示的 GET /books 端点。

r.GET("/books", func(c *gin.Context) {
	c.JSON(http.StatusOK, books)
})

请注意,我们可以简单地传递我们创建的 Slice 以返回响应的图书列表。

现在,完整的 main.go 文件看起来像这样。


package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

type Book struct {
	ID     string `json:"id"`
	Title  string `json:"title"`
	Author string `json:"author"`
}

var books = []Book{
	{ID: "1", Title: "Harry Potter", Author: "J. K. Rowling"},
	{ID: "2", Title: "The Lord of the Rings", Author: "J. R. R. Tolkien"},
	{ID: "3", Title: "The Wizard of Oz", Author: "L. Frank Baum"},
}

func main() {
	r := gin.New()

	r.GET("/books", func(c *gin.Context) {
		c.JSON(http.StatusOK, books)
	})

	r.Run()
}

运行代码以启动API服务器。

go run main.go

通过在浏览器中输入URL http://localhost:8080/books 来测试API,你应该会看到我们的图书列表。

3. 实现POST端点以创建图书。

然后实现 POST /albums

r.POST("/books", func(c *gin.Context) {
	var book Book

	if err := c.ShouldBindJSON(&book); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err.Error(),
		})
		return
	}

	books = append(books, book)

	c.JSON(http.StatusCreated, book)
})

此处理程序需要接收请求体以创建新的图书,我们可以使用 c.ShouldBindJSON 方法并将指针 &book 传递给它,以将数据体绑定到我们的结构中。

还要注意,c.ShouldBindJSON 将验证请求体并在存在任何验证问题时返回错误。我们将处理它以返回 400 Bad Request 状态和我们的错误消息。

之后,我们将新的 Book Struct 添加到 Slice 中,然后返回新创建的 Book Struct

现在,完整的 main.go 文件看起来像这样。

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

type Book struct {
	ID     string `json:"id"`
	Title  string `json:"title"`
	Author string `json:"author"`
}

var books = []Book{
	{ID: "1", Title: "Harry Potter", Author: "J. K. Rowling"},
	{ID: "2", Title: "The Lord of the Rings", Author: "J. R. R. Tolkien"},
	{ID: "3", Title: "The Wizard of Oz", Author: "L. Frank Baum"},
}

func main() {
	r := gin.New()

	r.GET("/books", func(c *gin.Context) {
		c.JSON(http.StatusOK, books)
	})

	r.POST("/books", func(c *gin.Context) {
		var book Book

		if err := c.ShouldBindJSON(&book); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"error": err.Error(),
			})
			return
		}

		books = append(books, book)

		c.JSON(http.StatusCreated, book)
	})

	r.Run()
}
view raw

现在,我们将测试 post 端点,但是在浏览器中输入URL将是 get 请求。因此,我们需要另一个工具来测试 post 请求。

有许多替代工具可用,但我将使用名为 REST Client 的VSCode扩展。

然后,你可以创建一个类似于以下内容的 test.http 文件。

现在,你可以发送 post 请求,结果如下所示。

我们再次使用扩展程序测试 http://localhost:8080/booksget,你应该会看到新创建的图书。

请注意,创建的图书将不是持久的,这意味着如果我们重新启动API服务并再次运行 go run main.go,则创建的图书将丢失。(我们将在本教程系列的下一篇文章中讨论如何持久化数据)。

4. 实现DELETE端点

最后一个端点是DELETE /albums/:id

r.DELETE("/books/:id", func(c *gin.Context) {
	id := c.Param("id")

	for i, a := range books {
		if a.ID == id {
			books = append(books[:i], books[i+1:]...)
			break
		}
	}

	c.Status(http.StatusNoContent)
})

这次,我们必须从请求的URL中获取路径参数,我们可以使用路径中的 :id 并使用 c.Param("id")``` 方法在处理程序中获取它。然后我们实现了根据书籍id从切片中删除书籍的逻辑,然后向客户端返回204 No Content` http状态。

完整的main.go文件现在如下所示。

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

type Book struct {
	ID     string `json:"id"`
	Title  string `json:"title"`
	Author string `json:"author"`
}

var books = []Book{
	{ID: "1", Title: "Harry Potter", Author: "J. K. Rowling"},
	{ID: "2", Title: "The Lord of the Rings", Author: "J. R. R. Tolkien"},
	{ID: "3", Title: "The Wizard of Oz", Author: "L. Frank Baum"},
}

func main() {
	r := gin.New()

	r.GET("/books", func(c *gin.Context) {
		c.JSON(http.StatusOK, books)
	})

	r.POST("/books", func(c *gin.Context) {
		var book Book

		if err := c.ShouldBindJSON(&book); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"error": err.Error(),
			})
			return
		}

		books = append(books, book)

		c.JSON(http.StatusCreated, book)
	})

	r.DELETE("/books/:id", func(c *gin.Context) {
		id := c.Param("id")

		for i, a := range books {
			if a.ID == id {
				books = append(books[:i], books[i+1:]...)
				break
			}
		}

		c.Status(http.StatusNoContent)
	})

	r.Run()
}

另外,我们将通过向test.http文件添加删除请求来测试我们的API的结果。

首先运行GET端点以查看我们的书籍列表

然后运行DELETE端点

然后再次运行GET端点以检查结果,我们将看到第二本书已从我们的书籍列表中删除。

重构代码

为了使代码更易读,我们将把处理程序函数提取到它自己的函数中。

r.GET("/books", listBooksHandler)
r.POST("/books", createBookHandler)
r.DELETE("/books/:id", deleteBookHandler)
func listBooksHandler(c *gin.Context) {
	c.JSON(http.StatusOK, books)
}

func createBookHandler(c *gin.Context) {
	var book Book

	if err := c.ShouldBindJSON(&book); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err.Error(),
		})
		return
	}

	books = append(books, book)

	c.JSON(http.StatusCreated, book)
}

func deleteBookHandler(c *gin.Context) {
	id := c.Param("id")

	for i, a := range books {
		if a.ID == id {
			books = append(books[:i], books[i+1:]...)
			break
		}
	}

	c.Status(http.StatusNoContent)
}

并将Book结构体和切片移到主函数下。

func main() {
	r := gin.New()

	r.GET("/books", listBooksHandler)
	r.POST("/books", createBookHandler)
	r.DELETE("/books/:id", deleteBookHandler)

	r.Run()
}

type Book struct {
	ID     string `json:"id"`
	Title  string `json:"title"`
	Author string `json:"author"`
}

var books = []Book{
	{ID: "1", Title: "Harry Potter", Author: "J. K. Rowling"},
	{ID: "2", Title: "The Lord of the Rings", Author: "J. R. R. Tolkien"},
	{ID: "3", Title: "The Wizard of Oz", Author: "L. Frank Baum"},
}

完成了!我们使用Gin框架创建了一个简单的API服务。

最终代码

最终的main.go文件如下所示。

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.New()

	r.GET("/books", listBooksHandler)
	r.POST("/books", createBookHandler)
	r.DELETE("/books/:id", deleteBookHandler)

	r.Run()
}

type Book struct {
	ID     string `json:"id"`
	Title  string `json:"title"`
	Author string `json:"author"`
}

var books = []Book{
	{ID: "1", Title: "Harry Potter", Author: "J. K. Rowling"},
	{ID: "2", Title: "The Lord of the Rings", Author: "J. R. R. Tolkien"},
	{ID: "3", Title: "The Wizard of Oz", Author: "L. Frank Baum"},
}

func listBooksHandler(c *gin.Context) {
	c.JSON(http.StatusOK, books)
}

func createBookHandler(c *gin.Context) {
	var book Book

	if err := c.ShouldBindJSON(&book); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err.Error(),
		})
		return
	}

	books = append(books, book)

	c.JSON(http.StatusCreated, book)
}

func deleteBookHandler(c *gin.Context) {
	id := c.Param("id")

	for i, a := range books {
		if a.ID == id {
			books = append(books[:i], books[i+1:]...)
			break
		}
	}

	c.Status(http.StatusNoContent)
}

译自:https://medium.com/@wattanai.tha/go-tutorial-series-ep-1-building-rest-api-with-gin-7c17c7ab1d5b

版权声明:本文内容由TeHub注册用户自发贡献,版权归原作者所有,TeHub社区不拥有其著作权,亦不承担相应法律责任。 如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

点赞(0)
收藏(0)
菜鸟一只
你就是个黄焖鸡,又黄又闷又垃圾。

评论(0)

添加评论