技术概述
本博客介绍使用gin框架完成基础的数据解析与绑定功能,以及列举出一些比较容易踩的坑。主要内容包括:json数据解析与绑定,表单数据解析与绑定,url数据解析与绑定
技术详述
1. json数据解析与绑定
先看官方文档中的源代码:
// 定义接收数据的结构体
type Login struct {
User string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"`
Pssword string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"`
}
func main() {
r := gin.Default()
// JSON绑定
r.POST("loginJSON", func(c *gin.Context) {
var json Login
// 将request的body中的数据,自动按照json格式解析到结构体
if err := c.ShouldBindJSON(&json); err != nil {
// 返回错误信息
// gin.H封装了生成json数据的工具
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 判断用户名密码是否正确
if json.User != "root" || json.Pssword != "admin" {
c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "200"})
})
r.Run(":8000")
}
这是一个简单的获取json中的账号密码,并进行验证的例子。从该例子中可以看出:
- 获取json数据时不必通过输入json中每个key值来获得对应的数据
- 结构体需要与获取json的格式相对应
- 返回结果可以使用gin框架中封装的方法
- post方法中需要一个与上下文(context)有关的参数,用于接收前端发来的json数据
于是,我们可以:
- 将service服务的结构体中的数据与需要从前端获取的参数一一对应,而删除掉多余的数据
- 声明一个函数用于json数据获取,解析与绑定,并调用相应的服务,将
c *gin.Context
作为方程的参数 - 将service服务的结构体与接收的json进行绑定
- 调用service服务处理数据
- 返回结果
下面用一个比较完整的程序片段来阐述具体用法:
-
service结构体中的数据
type CreateTeamService struct { model.TeamRepositoryInterface // 声明了具体操作数据库创建团队的接口 Name string `form:"name" json:"name" binding:"required"` GroupLeaderID int `form:"group_leader_id" json:"group_leader_id" binding:"required"` ClassID int `form:"class_id" json:"class_id" binding:"required"` }
-
post方法响应请求
// 创建团队 v1.POST("team/create", api.CreateTeam)
-
json数据解析与绑定具体函数
func CreateTeam(c *gin.Context) { // 该函数与CreateTeamService服务在不同的文件中 var service service.CreateTeamService if err := c.ShouldBind(&service); err == nil { // 实例化接口 service.TeamRepositoryInterface = &model.Repo res := service.CreateTeam() c.JSON(http.StatusOK, res) } else { c.JSON(http.StatusOK, ErrorResponse(err)) } }
可以看出,大致过程与上述官方文档中的用例一致。
2. 表单数据解析和绑定
具体代码与json数据解析与绑定几乎完全一致,不同处仅仅在于将ShouldBindJSON()
方法换成了Bind()
方法。
这也是gin框架的强大之处,即面对不同的前端数据有统一的代码处理方法。其本质上,还是将表单数据中每个数据的key值与结构中的数据一一对应。
-
表单结构示例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <form action="http://localhost:8000/loginForm" method="post" enctype="application/x-www-form-urlencoded"> 用户名<input type="text" name="username"><br> 密码<input type="password" name="password"> <input type="submit" value="提交"> </form> </body> </html>
-
结构体示例
type Login struct { // binding:"required"修饰的字段,若接收为空值,则报错,是必须字段 User string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"` Pssword string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"` }
3. URI数据解析和绑定
具体代码与上述两种方式大致相同,同样地,需要将ShouldBindJSON()
方法换成ShouldBindUri
方法。
除此之外,需要注意获取api参数的方法,在此仅介绍最简单的一种方法:
-
获取api参数代码
(测试url:
localhost:8080/user/乌鸡/805376577.com
)r.GET("/user/:name/*action", func(c *gin.Context) { name := c.Param("name") action := c.Param("action") //截取/ action = strings.Trim(action, "/") c.String(http.StatusOK, name+" is "+action) })
输出结果:乌鸡 is 805376577.com
注意事项&最佳实践
- 一定要在使用service服务的函数中实例化结构体中的接口,不然会报空指针的错误
- 注意结构体中的数据类型,严格按照接口文档中的数据类型设计,同时,在“json”或“form“属性中填入相应的”key“值,做到一一对应
- 一定要在接收数据时加入判错处理
- 最好不要在一个函数中调用两个service服务(实际上也很难做到)
- 返回给前端的结果可以考虑用
http.StatusOK
替代200
总结
gin框架给代码的编写带来了诸多方便,不仅代码简单友好,并且在程序出错的情况下也可以通过报错快速找到解决措施,因为gin的报错都很详细,我推荐使用go语言可以多多使用gin框架。
虽然网上gin的官方文档都介绍的比较详细,但是关于gin的个人教程以及问答帖子在国内的数量比较少,所以有时候出了问题比较难在网上直接找到解决方法。好在gin语法简单,只要有一定编程基础,应该都能够独立解决大部分问题。