今天学习下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") }