# 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方法终止请求,后面的处理逻辑不执行