# ginstudy
**Repository Path**: bigberg/ginstudy
## Basic Information
- **Project Name**: ginstudy
- **Description**: 记录go-gin 项目学习过程
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2023-07-11
- **Last Updated**: 2024-07-23
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
GinStudy
### 一、安装gin
#### 1.1 新建目录并初始化mod
go mod init ginstudy
#### 1.2 下载gin
go get -u github.com/gin-gonic/gin
### 二、启动一个简单的web服务
#### 2.1 启动web服务
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
// 1. 创建一个路由引擎
// 2. 添加路由到路由引擎,以及指定该路由的处理逻辑
r := gin.Default() // 路由引擎
// r.GET 请求方法 GET POST DELETE HEAD等
// /ping 请求路径
// GET("该条路由的路径","处理该路由的逻辑")
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
// 状态码 http.StatusOK
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Welcome 首页")
})
// 3. 启动Gin服务
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
#### 2.2 使用独立函数处理路由逻辑
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
// 定义一个函数,用于gin处理相关请求逻辑,并返回json数据
func getUserInfo(c *gin.Context) {
userInfo := make(map[string]string)
userInfo["name"] = "bigberg"
userInfo["age"] = "20"
userInfo["address"] = "南京"
c.JSON(http.StatusOK, userInfo)
}
func main() {
// 创建路由
r := gin.Default()
// 使用独立函数处理路由逻辑
r.GET("/api/user/info", getUserInfo)
r.Run()
}
返回的结果
{
"address": "南京",
"age": "20",
"name": "bigberg"
}
#### 2.3 规范JSON数据格式
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
/*
对于返回的json字符串,前后端会有一些约定
{
"status" : 10200,
"message": "查询成功",
"data": {
"name": "bigberg",
"age": "20",
"address": "南京"
}
}
*/
// 定义一个函数,用于gin处理相关请求逻辑,并返回json数据
func getUserInfo(c *gin.Context) {
// 定义返回数据
returnData := make(map[string]interface{})
returnData["status"] = 10200
returnData["message"] = "查询成功"
userInfo := make(map[string]string)
userInfo["name"] = "bigberg"
userInfo["age"] = "20"
userInfo["address"] = "南京"
returnData["data"] = userInfo
c.JSON(http.StatusOK, returnData)
}
func main() {
// 创建路由
r := gin.Default()
// 使用独立函数处理路由逻辑
r.GET("/api/user/info", getUserInfo)
r.Run()
}
返回的结果
{
"data": {
"address": "南京",
"age": "20",
"name": "bigberg"
},
"message": "查询成功",
"status": 10200
}
#### 2.4 使用结构体返回数据
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
/*
以结构体的形式将数据返回给前端
1. 定义的结构体属性使用大写开头,因为这样其他包也能引用
2. `json:"username"` 可以让返回的json字符串中,显示我们重新定义的属性名称
*/
type UserInfo struct {
Username string `json:"username"`
Age int `json:"age"`
Adderss string `json:"address"`
}
type ReturnData struct {
Status int `json:"status"`
Message string `json:"message"`
Data UserInfo `json:"data"`
}
// 定义一个函数,用于gin处理相关请求逻辑,并返回json数据
func getUserInfo(c *gin.Context) {
// 定义返回数据
userInfo := UserInfo{
Username: "bigberg",
Age: 20,
Adderss: "南京",
}
returnData := ReturnData{
Status: 10200,
Message: "查询成功",
Data: userInfo,
}
c.JSON(http.StatusOK, returnData)
}
func main() {
// 创建路由
r := gin.Default()
// 使用独立函数处理路由逻辑
r.GET("/api/user/info", getUserInfo)
r.Run()
}
返回的结果
{
"status": 10200,
"message": "查询成功",
"data": {
"username": "bigberg",
"age": 20,
"address": "南京"
}
}
### 三、请求方法
#### 3.1 简单的POST请求
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func addArticle(c *gin.Context) {
msg := "创建成功"
c.JSON(http.StatusOK, msg)
}
func main() {
// 创建路由
r := gin.Default()
// 路由路径和处理逻辑
r.POST("/api/article/add", addArticle)
r.Run()
}
使用postman调用"/api/article/add",就会返回"创建成功"的信息
#### 3.2 Gin获取GET请求参数
package main
import (
"fmt"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
/*
1、在url中拼接参数:请求路径+"?"后面接参数,多个参数使用"&"拼接
*/
func getUserInfo(c *gin.Context) {
userinfo := make(map[string]interface{})
// c.Query()获取传入的参数信息
u := c.Query("username")
id := c.Query("id")
// 将id 从 string转换成int
idNum, _ := strconv.Atoi(id)
userinfo["username"] = u
userinfo["id"] = id
fmt.Printf("用户名是:%s,用户id:%d\n", u, idNum)
c.JSON(http.StatusOK, userinfo)
}
func main() {
// 创建路由
r := gin.Default()
// 路由路径和处理逻辑
r.GET("/api/user/info", getUserInfo)
r.Run()
}
请求参数
/api/user/info?username=bigberg&id=1
#### 3.3 POST请求form-data参数
package main
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
/*
POST 参数
JSON 格式,数据量多
form-data 表单形式
*/
type UserInfo struct {
Username string `json:"username"`
ID int `json:"id"`
Age int `json:"age"`
Address string `json:"address"`
}
type ReturnData struct {
Status int `json:"status"`
Message string `json:"message"`
Data UserInfo `json:"data"`
}
func updateUserInfo(c *gin.Context) {
username := c.PostForm("username")
id, _ := strconv.Atoi(c.PostForm("id"))
age, _ := strconv.Atoi(c.PostForm("age"))
// 如果没有传参,给个默认值
// c.DefaultQuery() 也可以设置默认值
address := c.DefaultPostForm("address", "中国")
userinfo := UserInfo{}
userinfo.ID = id
userinfo.Username = username
userinfo.Age = age
userinfo.Address = address
returnData := ReturnData{
Status: 10200,
Message: "创建成功",
Data: userinfo,
}
c.JSON(http.StatusOK, returnData)
}
func main() {
// 创建路由
r := gin.Default()
// 路由路径和处理逻辑
r.POST("/api/user/updateinfo", updateUserInfo)
r.Run()
}
返回的结果,使用postman发送请求和参数
{
"status": 10200,
"message": "创建成功",
"data": {
"username": "bigberg",
"id": 1,
"age": 20,
"address": "南京"
}
}
#### 3.4 POST传入JSON类型参数
package main
import (
"encoding/json"
"net/http"
"github.com/gin-gonic/gin"
)
/*
使用JSON提交数据
*/
var mjson map[string]interface{}
func addUseByJson(c *gin.Context) {
//POST获取json参数
//GetRawData 返回两个参数,一个byte类型,一个error类型
jsonData, _ := c.GetRawData()
// 获取的json字符串,有两种处理方法
// 1. 转换成map, Unmarshal可以接受一个byte类型,并存储到一个指针中
// 2. 绑定到结构体中
json.Unmarshal(jsonData, &mjson)
c.JSON(http.StatusOK, mjson)
// c.JSON(http.StatusOK, string(jsonData))
}
func main() {
// 创建路由
r := gin.Default()
// 路由路径和处理逻辑
r.POST("/api/user/adduserbyjson", addUseByJson)
r.Run()
}
#### 3.5 接受的JSON参数绑定结构体
package main
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
/*
模型绑定
可以接受请求的数据直接绑定到结构体,最常用的方式
*/
type UserInfo struct {
Id int `json:"id"`
Username string `json:"username"`
Age int `json:"age"`
Address string `json:"address"`
}
type ReturnData struct {
Status int `json:"status"`
Message string `json:"message"`
Data UserInfo `json:"data"`
}
func addUserBindstruct(c *gin.Context) {
returnData := ReturnData{}
// c.ShouldBindJSON, 接受一个指针类型,并会返回一个error信息
if err := c.ShouldBindJSON(&returnData); err != nil {
// 说明绑定失败
fmt.Println("数据绑定失败:", err.Error())
c.JSON(http.StatusOK, gin.H{
"message": "参数格式不正确",
"status": 500,
})
} else {
// fmt.Println(returnData)
c.JSON(http.StatusOK, returnData)
}
}
func main() {
// 创建路由
r := gin.Default()
// 路由路径和逻辑处理
r.POST("/api/user/adduserbindstruct", addUserBindstruct)
r.Run()
}
### 四、Gin路由分组
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
/*
Gin 路由分组
对于请求的接口,很多具有相同的请求路径
可以根据具体的功能,将这些请求进行分组处理
/api/user/getinfo
/api/user/add
/api/user/update
/api/user/del
/api/product/getinfo
/api/product/add
/api/product/update
/api/product/del
*/
func getUserInfo(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"username": "bigberg",
"age": 20,
"address": "南京",
})
}
func getProductInfo(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"prodoctname": "apple",
"numbers": 200,
"price": "15元/斤",
})
}
func main() {
// 创建路由
r := gin.Default()
// 分组
apiGroup := r.Group("/api")
userGroup := apiGroup.Group("/user")
productGroup := apiGroup.Group("/product")
// 逻辑处理
userGroup.GET("/getinfo", getUserInfo)
productGroup.GET("/getinfo", getProductInfo)
r.Run()
}
使用postman调用接口,返回的结果
{
"numbers": 200,
"price": "15元/斤",
"prodoctname": "apple"
}
### 五、Gin的中间件
在Gin的整个实现中,中间件可谓是Gin的精髓。一个个中间件组成一条中间件链,对HTTP Request请求进行拦截处理,实现了代码的解耦和分离,并且中间件之间相互不用感知到,每个中间件只需要处理自己需要处理的事情即可
#### 5.1 全局中间件
package main
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
func getUserInfo(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"username": "bigberg",
"age": 20,
"address": "南京",
})
}
func getProductInfo(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"prodoctname": "apple",
"numbers": 200,
"price": "15元/斤",
})
}
// 定义中间件
func globalMiddleWare(c *gin.Context) {
token := c.Query("token")
if token == "" {
// 没有带token
c.String(401, "用户未登录")
// 如果没有c.Abort(),即便没有token程序也不会终止
c.Abort() // 终止请求
} else {
c.String(http.StatusOK, "用户已登录")
}
//c.Abort()后,这边的代码也会被执行
fmt.Println("请先登录")
}
func main() {
// 创建路由
r := gin.Default()
// 分组
apiGroup := r.Group("/api")
/*
全局中间件
对于apiGroup接口的请求,都验证是否有token
*/
apiGroup.Use(globalMiddleWare)
userGroup := apiGroup.Group("/user")
productGroup := apiGroup.Group("/product")
// 逻辑处理
userGroup.GET("/getinfo", getUserInfo)
productGroup.GET("/getinfo", getProductInfo)
r.Run()
}
使用postman调用api接口,如果没有传入token,则返回"用户未登录"
#### 5.2 局部中间件
package main
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
/*
模型绑定
可以接受请求的数据直接绑定到结构体,最常用的方式
*/
type UserInfo struct {
Id int `json:"id"`
Username string `json:"username"`
Token string `json:"token"`
Age int `json:"age"`
Address string `json:"address"`
}
var userinfo UserInfo
/*
验证token,如果token不存在,则终止请求
*/
func tokenIdentified(c *gin.Context) {
if err := c.ShouldBindJSON(&userinfo); err != nil {
fmt.Println("数据绑定失败:", err.Error())
c.JSON(http.StatusOK, gin.H{
"message": "参数格式不正确",
"status": 500,
})
} else {
if userinfo.Token == "" {
c.JSON(401, gin.H{
"message": "用户未登录,请先登录",
})
c.Abort() // 终止请求
} else {
c.JSON(http.StatusOK, gin.H{
"message": "登录成功",
})
}
}
}
/*
验证用户权限,只有admin账号有删除权限
如果不是admin,则终止请求
*/
func withAuth(c *gin.Context) {
if userinfo.Username != "admin" {
c.JSON(403, gin.H{
"message": "该用户没有权限执行此操作",
})
c.Abort()
} else {
c.JSON(http.StatusOK, gin.H{
"message": "正在进行删除操作",
})
}
}
/*
用户删除
*/
func delUser(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "用户删除成功",
})
}
func main() {
// 创建路由
r := gin.Default()
// 路由路径和逻辑处理
apiGroup := r.Group("/api")
// 全局中间件,对/api接口的访问进行token验证
apiGroup.Use(tokenIdentified)
userGroup := apiGroup.Group("/user")
// 局部中间件,对删除操作进行用户权限验证
userGroup.POST("/del", withAuth, delUser)
r.Run()
}
#### 5.3 多个中间件
多个中间件的执行是顺序执行的
userGroup.POST("/del", middleware1, middleware2,delUser)
程序依次执行 middleware1 middleware2 delUser
#### 5.4 Next和Abort方法
Next方法是先进后出,Next方法后面的语句,将会在其他处理逻辑执行完成后,再回过来执行
Abort方法终止请求,后面的处理逻辑不执行