Golang 使用Gin创建Restful API
今天学习下Go语言如何集成Gin框架编写Restful Web API的基本操作。Gin框架简化了Go原生语言构建Web应用程序的复杂度,在今天的学习中,将学会使用Gin构建路由请求、数据检索、JSON响应封装等最简单的Web服务。
基本要求
- Go 1.16 及更高版本
- 合适的编译工具 - text编辑器也满足要求
- 命令终端 - Linux、Mac系统shell, Windows系统的Cmd、PowerShell
- curl 工具 - curl 是一个利用URL语法在命令行下工作的文件传输工具
设计API
遵循Restful API 架构风格,构建以下两个Http Api:
- /albums
- GET - 获取数据列表,以JSON格式返回
- POST - 接收客户端发送的JSON请求,新增数据项
- /albums/:id
- GET - 根据指定ID获取特定的数据,跟SpringBoot框架动态ID使用 {id} 不同,Gin框架在语法上使用 冒号: 表明该参数为为前端传递的动态参数
代码开发
创建项目
- 创建项目目录
$ mkdir web-service-gin
$ cd web-service-gin
- 项目初始化 - 使用go mod init 命令初始化
$ go mod init example/web-service-gin
该命令会自动创建go.mod文件,该文件用于管理Go应用中的依赖,作用类似于Java语言中的Maven
创建数据格式
为了简化Demo开发难度,将直接使用内存中的数据,不跟DB进行交互(真实项目中不推荐)。首先在项目根目录下创建main.go文件,文件内容如下:
package main
// 定义JSON 返回格式
type album struct {
ID string `json:"id"`
Title string `json:"title"`
Artist string `json:"artist"`
Price float64 `json:"price"`
}
// 内存中存储的数组
var albums = []album{
{ID: "1", Title: "Blue Train", Artist: "John Coltrane", Price: 56.99},
{ID: "2", Title: "Jeru", Artist: "Gerry Mulligan", Price: 17.99},
{ID: "3", Title: "Sarah Vaughan and Clifford Brown", Artist: "Sarah Vaughan", Price: 39.99},
}
Restful API
返回数据列表
当客户端使用Get方式请求**/albums**路径时,需要按照JSON格式返回所有数据(这里先不讨论分页)。实现该需求,代码开发时,需要注意以下两点
- 准备响应逻辑
- 将请求路径跟响应逻辑进行匹配
- 处理函数
// 在main.go新增函数
// getAlbums responds with the list of all albums as JSON.
func getAlbums(c *gin.Context) {
c.IndentedJSON(http.StatusOK, albums)
}
代码说明:
- 编写getAlbums函数,该函数接受gin.Context参数。您可以为该函数指定任何你喜欢的函数名称。gin.Context是Gin框架中最重要的部分,它携带HTTP Request请求的所有细节,如请求参数、验证、JSON序列化等
- 调用Context.IndedJSON将结构序列化为JSON并将其添加到响应中。Context.IndedJSON函数的第一个参数是要发送给客户端的HTTP状态代码。在这里默认为200,表示请求成功
- **路由处理 **
// 在 main.go 文件中新增
func main() {
router := gin.Default()
router.GET("/albums", getAlbums)
router.Run("localhost:8080")
}
代码说明
- 使用默认方式初始化Gin Router路由
- 使用GET方法关联**/albums** 和 getAlbums 函数
- 调用Run函数启动服务器
新增依赖
// 在 main.go 文件中新增
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
- 运行服务
- 添加依赖 - 使用以下命令 拉取Gin框架依赖包
$ go get .
- 运行服务
$ go run .
- 使用curl工具发送Http请求 - 打开另外的终端发送请求
curl http://localhost:8080/albums
新增数据项
使用同样的方式,在服务器端编写POST请求接收客户端数据新增数据项。跟之前Get请求稍微不同的是,该请求需要从request对象中解析出Body信息
- 处理函数
// postAlbums adds an album from JSON received in the request body.
func postAlbums(c *gin.Context) {
var newAlbum album
// 调用BindJSON方法将数据解析到 newAlbum变量中
if err := c.BindJSON(&newAlbum); err != nil {
return
}
// 将数据追加到内存数组中
albums = append(albums, newAlbum)
c.IndentedJSON(http.StatusCreated, newAlbum)
}
- 路由处理
func main() {
router := gin.Default()
router.GET("/albums", getAlbums)
router.POST("/albums", postAlbums)
router.Run("localhost:8080")
}
- 运行服务
$ go run .
- 发送客户端请求
$ curl http://localhost:8080/albums \
--include \
--header "Content-Type: application/json" \
--request "POST" \
--data '{"id": "4","title": "The Modern Sound of Betty Carter","artist": "Betty Carter","price": 49.99}'
此时,在调用获取数据列表的接口,必须返回4个数据了
返回指定数据
当客户端以GET请求方式调用 **/albums/[id]**路径,服务端需要返回指定ID的数据详情。此时该ID是由客户端动态指定的,接下来看看如何实现
- 处理函数
// getAlbumByID locates the album whose ID value matches the id
// parameter sent by the client, then returns that album as a response.
func getAlbumByID(c *gin.Context) {
id := c.Param("id")
// Loop over the list of albums, looking for
// an album whose ID value matches the parameter.
for _, a := range albums {
if a.ID == id {
c.IndentedJSON(http.StatusOK, a)
return
}
}
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "album not found"})
}
- 路由匹配
func main() {
router := gin.Default()
router.GET("/albums", getAlbums)
router.GET("/albums/:id", getAlbumByID)
router.POST("/albums", postAlbums)
router.Run("localhost:8080")
}
- 运行服务
$ go run .
- 客户端请求
$ curl http://localhost:8080/albums/2
完整代码
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
// album represents data about a record album.
type album struct {
ID string `json:"id"`
Title string `json:"title"`
Artist string `json:"artist"`
Price float64 `json:"price"`
}
// albums slice to seed record album data.
var albums = []album{
{ID: "1", Title: "Blue Train", Artist: "John Coltrane", Price: 56.99},
{ID: "2", Title: "Jeru", Artist: "Gerry Mulligan", Price: 17.99},
{ID: "3", Title: "Sarah Vaughan and Clifford Brown", Artist: "Sarah Vaughan", Price: 39.99},
}
func getAlbums(c *gin.Context) {
c.IndentedJSON(http.StatusOK, albums)
}
// postAlbums adds an album from JSON received in the request body.
func postAlbums(c *gin.Context) {
var newAlbum album
// Call BindJSON to bind the received JSON to
// newAlbum.
if err := c.BindJSON(&newAlbum); err != nil {
return
}
// Add the new album to the slice.
albums = append(albums, newAlbum)
c.IndentedJSON(http.StatusCreated, newAlbum)
}
// getAlbumByID locates the album whose ID value matches the id
// parameter sent by the client, then returns that album as a response.
func getAlbumByID(c *gin.Context) {
id := c.Param("id")
// Loop over the list of albums, looking for
// an album whose ID value matches the parameter.
for _, a := range albums {
if a.ID == id {
c.IndentedJSON(http.StatusOK, a)
return
}
}
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "album not found"})
}
func main() {
router := gin.Default()
router.GET("/albums", getAlbums)
router.POST("/albums", postAlbums)
router.GET("/albums/:id", getAlbumByID)
router.Run("localhost:8080")
}