一、前言¶
本文主要以下几方面介绍Gin相关基础知识:
- 为什么要使用框架写项目
- 常见的Go语言框架及对比
- 前后端分离
- 什么是JSON
- HTTP请求类型介绍
二、为什么要使用框架写项目¶
在 Go 语言中,使用框架来开发项目有许多好处:
- 提高开发效率: 框架提供了常见功能的现成实现,减少了重复编码的工作。这使开发者能够更快速地开发应用程序。
- 可维护性: 框架通常遵循良好的代码结构和设计模式,有助于提高代码的可读性和维护性。
- 性能优化: 框架通常会针对性能进行优化,包括路由、中间件处理等方面,可以提供更好的性能。
- 安全性: 框架通常会提供一些安全性保护机制,如防止 SQL 注入、XSS 攻击等。
- 可扩展性: 大多数框架支持插件和中间件,使开发者能够轻松地扩展应用程序功能。
- 社区支持: 流行的框架通常有庞大的社区支持,可以获得问题解答、文档和扩展模块等。
- 标准化: 使用框架有助于遵循一些行业标准和最佳实践,从而减少了项目出现问题的可能性。
三、常见的Go语言框架及对比¶
常见的Go语言框架及对比:
Gin:
- 轻量级 Web 框架,性能出色,适用于快速开发。
- 它的 API 简单,易于学习和使用。
- 支持中间件,有丰富的社区插件。
- 缺点:对于大规模应用可能不够灵活。
Echo:
- 与 Gin 类似,轻量级且性能很好。
- 设计简单,易于入门。
- 支持中间件和路由组。
- 缺点:相对较小的社区。
Beego:
- 一个全栈 Web 框架,提供了路由、ORM、模板引擎等功能。
- 框架功能较全面,适合构建大型应用。
- 缺点:学习曲线较陡峭。
Iris:
- 一个性能强大的 Web 框架,支持异步请求处理。
- 提供了中间件、路由组、模板引擎等功能。
- 缺点:相对较新,社区相对较小。
Buffalo:
- 用于构建 Web、API 和后端服务的框架,提供生成器工具。
- 框架包括 ORM、模板引擎、前端构建工具等。
- 缺点:复杂性较高,适用于大型应用。
Revel:
- 全栈框架,提供了路由、ORM、模板引擎等功能。
- 自带开发工具,适合快速开发。
- 缺点:相对较大,学习曲线陡峭。
Fiber:
- 类似于 Express.js 的 Web 框架,性能出色。
- 提供了路由、中间件等功能。
- 简单易用,适用于构建高性能应用。
- 缺点:相对较新,社区支持尚在发展中。
小结:
选择框架取决于项目的性质和需求。如果需要快速开发小型应用,可以选择轻量级框架如 Gin 或 Echo。对于大型应用,可能需要全栈框架如 Beego 或 Revel。建议根据项目需求、个人偏好和团队熟悉度来选择适当的框架。
四、前后端分离¶
4.1 什么是前后端分离¶
前后端分离是一种软件架构模式,其中前端和后端的开发是分离的,它们彼此独立工作,通过网络接口进行通信。在这种模式下,前端负责用户界面和用户体验,通常使用 HTML、CSS 和 JavaScript 进行开发,而后端负责处理数据、业务逻辑和数据库访问。前后端通过API(Application Programming Interface)进行通信,前端发送请求,后端提供响应数据。这种分离的方式有助于提高开发团队的协作效率,允许使用不同技术栈来开发前端和后端,并提供更好的可维护性和扩展性。

在前后端分离框架中,通过 GET 请求查询数据并渲染的完整流程如下:

-
用户触发数据请求: 用户在前端界面执行操作(如点击按钮、页面加载),触发前端的数据获取逻辑(例如 Vue/React 组件的
mounted钩子或点击事件处理函数)。 -
前端发起 GET 请求: 前端通过 HTTP 客户端(如
axios、fetch)向后端 API 发送 GET 请求,携带必要参数(如分页page=1、筛选keyword=xxx等,通过查询字符串?附加在 URL 中)。示例请求:http
http
GET /api/data?page=1&size=10 HTTP/1.1
Host: example.com
-
后端处理请求:
-
后端 API 解析请求参数(如 Node.js 的
req.query、Django 的request.GET)。 -
根据参数执行数据库查询(如 SQL 语句
SELECT * FROM table WHERE ... LIMIT ...),通过数据库驱动(如mysql2、psycopg2)与数据库交互。 -
数据库返回查询结果: 数据库执行查询后,将结果(如多行数据)返回给后端。
-
后端响应 JSON 数据: 后端将查询结果转换为 JSON 格式(如数组、对象),并返回 HTTP 200 状态码。示例响应:json
json
{
"code": 200,
"data": [{"id": 1, "name": "xxx"}, ...],
"total": 100
}
- 前端解析并更新状态:
前端接收响应后,解析 JSON 数据,更新组件状态(如 Vue 的
data、React 的useState)。例如:javascript
javascript
axios.get('/api/data').then(res => {
this.list = res.data.data; // 更新列表数据
});
- 前端渲染视图:
基于更新后的状态,前端框架(如 Vue 的
v-for、React 的map)将数据渲染到 DOM 中,展示给用户(如列表渲染、表格填充)。

在前后端分离框架中,通过 POST 请求查询数据并渲染的完整流程如下:
-
用户触发操作 用户在前端界面执行复杂查询操作(如提交包含多条件筛选的表单、发起高级搜索),触发前端的数据查询逻辑(例如表单提交事件、按钮点击回调)。
-
前端收集请求参数(步骤 1) 前端从界面元素(如输入框、下拉菜单、复选框)中提取用户输入的查询参数(如
{ "keyword": "手机", "priceRange": [1000, 5000], "page": 2 }),整理为键值对结构。 -
前端构建请求体(步骤 2) 将收集的参数转换为 JSON 格式的请求体,确保数据结构符合后端 API 预期(如嵌套对象、数组等)。示例请求体:
json
json
{
"filters": {
"keyword": "手机",
"price": { "min": 1000, "max": 5000 }
},
"pagination": { "page": 2, "size": 10 },
"sort": { "field": "price", "order": "asc" }
}
- 前端发送 POST 请求(步骤 3)
使用 HTTP 客户端(如
axios)发送 POST 请求,将 JSON 请求体放入request body,设置Content-Type: application/json。请求示例:
http
```http POST /api/products/advanced-search HTTP/1.1 Content-Type: application/json Host: example.com
{ / 上述请求体JSON / } ```
-
后端解析请求体(步骤 4) 后端框架(如 Node.js Express 的
body-parser、Django 的JSONParser)解析请求体,提取 JSON 数据到内存对象(如req.body)。 -
后端参数校验与业务处理(步骤 5)
-
参数校验:验证数据格式(如价格范围是否合法、分页参数是否为正整数),使用校验库(如 Joi、Pydantic)确保输入符合规则。
-
业务处理:根据业务逻辑补充必要参数(如用户 ID、时间戳),或进行权限检查(如仅管理员可访问某些筛选条件)。
-
后端执行复杂查询(步骤 6) 根据请求参数构建数据库查询:
-
多表关联:如
SELECT p.*, c.name AS category FROM products p JOIN categories c ON p.category_id = c.id。 - 条件筛选:
WHERE p.price BETWEEN 1000 AND 5000 AND p.name LIKE '%手机%'。 -
排序与分页:
ORDER BY p.price ASC LIMIT 10 OFFSET 10(假设第 2 页,每页 10 条)。 通过数据库驱动(如mysql2、psycopg2)执行查询,获取原始数据(如 SQL 结果集)。 -
数据库返回原始数据(步骤 7) 数据库执行查询后,将结果(如多行记录)返回给后端。
-
后端数据加工与格式化(步骤 8)
-
数据转换:将原始数据转换为前端友好的格式(如驼峰命名、嵌套结构展开)。
- 添加元信息:计算总记录数(
SELECT COUNT(*) FROM products ...),组装分页信息({ "total": 125, "page": 2, "size": 10 })。 最终数据结构示例:
json
json
{
"code": 200,
"message": "查询成功",
"data": [
{ "id": 1, "name": "智能手机", "price": 2999, "category": "数码" },
// ... 其他数据
],
"pagination": { "total": 125, "page": 2, "size": 10 }
}
-
后端返回 JSON 响应(步骤 9) 以 HTTP 200 状态码返回格式化后的 JSON 数据,完成后端处理流程。
-
前端解析响应数据(步骤 10) 前端接收响应后,解析 JSON 数据,分离
data(业务数据)、pagination(分页信息)等字段,存储到临时变量或状态管理库(如 Vuex、Redux)。 -
前端更新状态管理(步骤 11) 将解析后的数据更新到组件状态(如 Vue 的
data、React 的useState),或全局状态(如分页状态、筛选条件记忆)。例如:javascript
javascript axios.post('/api/...').then(res => { this.products = res.data.data; this.pagination = res.data.pagination; }); -
前端动态渲染视图(步骤 12) 基于更新后的状态,使用前端框架指令(如 Vue 的
v-for、React 的map)渲染数据:- 列表渲染:遍历
products数组,生成产品卡片或表格行。 - 分页控件:根据
pagination信息渲染页码、上一页 / 下一页按钮。 - 筛选反馈:展示当前筛选条件(如 “价格 1000-5000 元”),增强用户体验。
- 列表渲染:遍历
4.2 什么是JSON¶
JSON 是一种数据交换格式,通常用于前端和后端之间的数据交互。它采用文本格式,易于阅读和编写,同时也易于机器解析和生成。
JSON 数据是一种键-值对的结构,类似于字典或对象。每个键都是一个字符串,每个值可以是字符串、数字、布尔值、数组、对象等。
JSON 的一个重要特点是它是平台无关的,可以在各种编程语言中轻松解析和生成。
在前后端分离的架构中,前端通常通过发送包含 JSON 数据的 HTTP 请求,而后端将响应数据以 JSON 格式返回给前端,以实现数据交互。 JSON 已成为一种广泛采用的数据交互格式,用于传输配置信息、API 响应、日志记录等。
JSON示例:
{
"name": "John Smith",
"age": 25,
"email": "john.smith@example.com",
"address": {
"street": "123 Main Street",
"city": "Anytown",
"state": "CA",
"zip": "12345"
},
"phoneNumbers": [
{
"type": "home",
"number": "555-555-1234"
},
{
"type": "work",
"number": "555-555-5678"
}
],
"active": true
}
4.3 HTTP请求¶
HTTP(Hypertext Transfer Protocol)定义了一组请求方法(也称为HTTP请求类型),它们用于指定客户端对服务器执行的操作。每个HTTP请求类型都有特定的用途和应用场景。以下是一些常见的HTTP请求类型以及它们的具体应用场景:
-
GET:用于从服务器获取数据。通常用于请求页面、图像、样式表和其他资源。它是幂等的,不应该对服务器状态产生影响。
-
POST:用于向服务器发送数据,通常用于提交表单数据、上传文件、创建资源等。它不是幂等的,因为多次相同的POST请求可能会产生不同的结果。
- PUT:用于更新或创建指定资源。通常用于完全替代目标资源,因此需要客户端提供完整的更新数据。
- PATCH:用于部分更新资源。通常用于更新资源的一部分,而不是整个资源。它允许客户端提供需要更新的字段。
- DELETE:用于删除指定资源。通常用于删除资源,但要谨慎使用,因为它会永久删除资源。
- HEAD:类似于GET,但只返回响应头部,而不包含响应主体。通常用于检查资源的元数据,如内容类型或内容长度。
- OPTIONS:用于获取目标资源支持的通信选项。通常用于跨域资源共享(CORS)请求,以确定是否允许跨域请求。
五、如何使用Gin快速搭建一个web服务¶
参考链接:Gin Web Framework
1.本地自定义文件夹
本地上新建一个名为ginstudy的文件夹,使用VSCode打开。如果没有go.mod,需要执行以下命令在ginstudy目录下进行模块初始化,自动会在该目录下创建go.mod文件
$ go mod init ginstudy
2.安装gin
使用以下命令安装 gin
$ go get -u github.com/gin-gonic/gin
3.编写代码文件
定义main.go文件
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
// 1、首先创建一个路由引擎(方便之后可以在路由引擎中添加一些路由和路由的处理逻辑)
r := gin.Default()
// 2、其次添加路由至路由引擎,以及制定该路由的处理逻辑
// 2.1 添加路由路径
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
r.GET("/", func(c *gin.Context) {
// 状态码
c.String(http.StatusOK, "你在访问的是首页")
})
// 3、最后启动路由引擎
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
4.运行代码
$ go run .\main.go
5.访问测试
打开浏览器,输入127.0.0.1:8080/,提示输出"你在访问的是首页"字样

打开浏览器,输入127.0.0.1:8080/ping,提示输出如下字样:
{
"message": "pong"
}

在前端测试的同时,访问日志也会在后台显现出来

六、Gin数据交互JSON串内容规范¶
1.编写代码文件
基于上面内容重新定义main.go文件
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func getUserInfo(c *gin.Context) {
/*
{
"status": 200 //请求我已经收到了,处理也是正常的
"message": "获取成功", //msg
"data": {
"name": "zq",
"age": "18",
"address": "北京"
}
}
*/
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "查询成功"
userInfo := make(map[string]string)
userInfo["name"] = "zq"
userInfo["age"] = "18"
userInfo["address"] = "北京"
returnData["data"] = userInfo
c.JSON(http.StatusOK, userInfo)
}
func main() {
// 1、首先创建一个路由引擎(方便之后可以在路由引擎中添加一些路由和路由的处理逻辑)
r := gin.Default()
// 2、其次添加路由至路由引擎,以及制定该路由的处理逻辑
// 2.1 添加路由路径
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
r.GET("/", func(c *gin.Context) {
// 状态码
c.String(http.StatusOK, "你在访问的是首页")
})
r.GET("/api/user/info", getUserInfo)
// 3、最后启动路由引擎
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
2.运行代码
$ go run .\main.go
3.访问测试
打开浏览器,输入127.0.0.1:8080/api/user/info,提示输出如下内容信息
{
"address": "北京",
"age": "18",
"name": "zq"
}
七、Gin使用结构体返回数据给前端¶
1.编写代码文件
基于上面内容重新定义main.go文件
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func getUserInfo(c *gin.Context) {
/*
{
"status": 200 //请求我已经收到了,处理也是正常的
"message": "获取成功", //msg
"data": {
"name": "zq",
"age": "18",
"address": "北京"
}
}
*/
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "查询成功"
userInfo := make(map[string]string)
userInfo["name"] = "zq"
userInfo["age"] = "18"
userInfo["address"] = "北京"
returnData["data"] = userInfo
c.JSON(http.StatusOK, userInfo)
}
/*
1.用户发来请求,查询用户信息username="xxx"
2.我们的后端是不是要根据用户发来请求,获取到他要查询哪个用户的信息
3.后端从数据库查询到xxx用户详细信息
4.查询数据绑定到结构体,gorm,orm
*/
type UserInfo struct {
// json:后面无空格
Username string `json:"username"`
Age int `json:"age"`
Address string `json:"address"`
}
// returnData
type ReturnData struct {
Status int
Message string
Data UserInfo `json:"data"`
}
// 使用结构体返回数据给前端
func returnDataWithStruct(c *gin.Context) {
userInfo := UserInfo{
Username: "zq",
Age: 18,
Address: "北京",
}
c.JSON(http.StatusOK, userInfo)
}
func main() {
// 1、首先创建一个路由引擎(方便之后可以在路由引擎中添加一些路由和路由的处理逻辑)
r := gin.Default()
// 2、其次添加路由至路由引擎,以及制定该路由的处理逻辑
// 2.1 添加路由路径
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
r.GET("/", func(c *gin.Context) {
// 状态码
c.String(http.StatusOK, "你在访问的是首页")
})
r.GET("/api/user/info", getUserInfo)
r.GET("/api/user/info/WithStruct", returnDataWithStruct)
// 3、最后启动路由引擎
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
2.运行代码
$ go run .\main.go
3.访问测试
打开浏览器,输入http://127.0.0.1:8080/api/user/info/WithStruct,提示输出如下内容信息
{
"username": "zq",
"age": 18,
"address": "北京"
}
八、Gin配置POST类型的路由¶
1.编写代码文件
基于上面内容重新定义main.go文件
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func getUserInfo(c *gin.Context) {
/*
{
"status": 200 //请求我已经收到了,处理也是正常的
"message": "获取成功", //msg
"data": {
"name": "zq",
"age": "18",
"address": "北京"
}
}
*/
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "查询成功"
userInfo := make(map[string]string)
userInfo["name"] = "zq"
userInfo["age"] = "18"
userInfo["address"] = "北京"
returnData["data"] = userInfo
c.JSON(http.StatusOK, userInfo)
}
/*
1.用户发来请求,查询用户信息username="xxx"
2.我们的后端是不是要根据用户发来请求,获取到他要查询哪个用户的信息
3.后端从数据库查询到xxx用户详细信息
4.查询数据绑定到结构体,gorm,orm
*/
type UserInfo struct {
// json:后面无空格
Username string `json:"username"`
Age int `json:"age"`
Address string `json:"address"`
}
// returnData
type ReturnData struct {
Status int
Message string
Data UserInfo `json:"data"`
}
func addUser(c *gin.Context) {
c.String(http.StatusOK, "这是一个POST请求")
}
// 使用结构体返回数据给前端
func returnDataWithStruct(c *gin.Context) {
userInfo := UserInfo{
Username: "zq",
Age: 18,
Address: "北京",
}
c.JSON(http.StatusOK, userInfo)
}
func main() {
// 1、首先创建一个路由引擎(方便之后可以在路由引擎中添加一些路由和路由的处理逻辑)
r := gin.Default()
// 2、其次添加路由至路由引擎,以及制定该路由的处理逻辑
// 2.1 添加路由路径
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
r.GET("/", func(c *gin.Context) {
// 状态码
c.String(http.StatusOK, "你在访问的是首页")
})
r.GET("/api/user/info", getUserInfo)
r.GET("/api/user/info/WithStruct", returnDataWithStruct)
r.POST("/api/user/add", addUser)
// 3、最后启动路由引擎
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
2.运行代码
$ go run .\main.go
3.访问测试
打开浏览器,输入http://127.0.0.1:8080/api/user/info/WithStruct,提示404报错信息,这是因为浏览器默认都是GET请求。
4.使用Postman工具进行POST请求测试
填写Uri内容:http://127.0.0.1:8080/api/user/add后,点击【Send】,观察到返回回显内容为"这是一个POST请求"
Postman下载链接:Postman: The World's Leading API Platform | Sign Up for Free

5.再次编写代码文件
基于上面内容重新定义main.go文件,添加addArticle函数
func addArticle(c *gin.Context) {
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "创建成功"
c.JSON(http.StatusOK, returnData)
}
r.POST("/api/addArticle/add", addArticle)
完整代码配置文件如下
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func getUserInfo(c *gin.Context) {
/*
{
"status": 200 //请求我已经收到了,处理也是正常的
"message": "获取成功", //msg
"data": {
"name": "zq",
"age": "18",
"address": "北京"
}
}
*/
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "查询成功"
userInfo := make(map[string]string)
userInfo["name"] = "zq"
userInfo["age"] = "18"
userInfo["address"] = "北京"
returnData["data"] = userInfo
c.JSON(http.StatusOK, userInfo)
}
/*
1.用户发来请求,查询用户信息username="xxx"
2.我们的后端是不是要根据用户发来请求,获取到他要查询哪个用户的信息
3.后端从数据库查询到xxx用户详细信息
4.查询数据绑定到结构体,gorm,orm
*/
type UserInfo struct {
// json:后面无空格
Username string `json:"username"`
Age int `json:"age"`
Address string `json:"address"`
}
// returnData
type ReturnData struct {
Status int
Message string
Data UserInfo `json:"data"`
}
func addUser(c *gin.Context) {
c.String(http.StatusOK, "这是一个POST请求")
}
func addArticle(c *gin.Context) {
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "创建成功"
c.JSON(http.StatusOK, returnData)
}
// 使用结构体返回数据给前端
func returnDataWithStruct(c *gin.Context) {
userInfo := UserInfo{
Username: "zq",
Age: 18,
Address: "北京",
}
c.JSON(http.StatusOK, userInfo)
}
func main() {
// 1、首先创建一个路由引擎(方便之后可以在路由引擎中添加一些路由和路由的处理逻辑)
r := gin.Default()
// 2、其次添加路由至路由引擎,以及制定该路由的处理逻辑
// 2.1 添加路由路径
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
r.GET("/", func(c *gin.Context) {
// 状态码
c.String(http.StatusOK, "你在访问的是首页")
})
r.GET("/api/user/info", getUserInfo)
r.GET("/api/user/info/WithStruct", returnDataWithStruct)
r.POST("/api/user/add", addUser)
r.POST("/api/addArticle/add", addArticle)
// 3、最后启动路由引擎
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
6.使用Postman工具进行POST请求测试
填写Uri内容:http://127.0.0.1:8080/api/addArticle/add后,点击【Send】,观察到返回回显内容如下
{
"message": "创建成功",
"status": 200
}
九、Gin获取GET请求参数¶
1.编写代码文件
基于上面内容重新定义main.go文件,添加GET请求参数
package main
import (
"fmt"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
func getUserInfo(c *gin.Context) {
/*
{
"status": 200 //请求我已经收到了,处理也是正常的
"message": "获取成功", //msg
"data": {
"name": "zq",
"age": "18",
"address": "北京"
}
}
*/
// 接收请求参数
u := c.Query("username")
address := c.Query("address")
a := c.Query("age")
id := c.Query("id")
idNumber, _ := strconv.Atoi(id)
fmt.Println("查询用户的ID是: ", idNumber)
// 去数据库中去查询用户信息
fmt.Println("拿到了用户名: ", u)
fmt.Println("拿到了地址: ", address)
fmt.Println("拿到了年龄: ", a)
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "查询成功"
userInfo := make(map[string]string)
userInfo["name"] = u
userInfo["age"] = a
userInfo["address"] = address
returnData["data"] = userInfo
c.JSON(http.StatusOK, userInfo)
}
/*
1.用户发来请求,查询用户信息username="xxx"
2.我们的后端是不是要根据用户发来请求,获取到他要查询哪个用户的信息
3.后端从数据库查询到xxx用户详细信息
4.查询数据绑定到结构体,gorm,orm
*/
type UserInfo struct {
// json:后面无空格
Username string `json:"username"`
Age int `json:"age"`
Address string `json:"address"`
}
// returnData
type ReturnData struct {
Status int
Message string
Data UserInfo `json:"data"`
}
func addUser(c *gin.Context) {
c.String(http.StatusOK, "这是一个POST请求")
}
func addArticle(c *gin.Context) {
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "创建成功"
c.JSON(http.StatusOK, returnData)
}
// 使用结构体返回数据给前端
func returnDataWithStruct(c *gin.Context) {
userInfo := UserInfo{
Username: "zq",
Age: 18,
Address: "北京",
}
c.JSON(http.StatusOK, userInfo)
}
func main() {
// 1、首先创建一个路由引擎(方便之后可以在路由引擎中添加一些路由和路由的处理逻辑)
r := gin.Default()
// 2、其次添加路由至路由引擎,以及制定该路由的处理逻辑
// 2.1 添加路由路径
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
r.GET("/", func(c *gin.Context) {
// 状态码
c.String(http.StatusOK, "你在访问的是首页")
})
r.GET("/api/user/info", getUserInfo)
r.GET("/api/user/info/WithStruct", returnDataWithStruct)
r.POST("/api/user/add", addUser)
r.POST("/api/addArticle/add", addArticle)
// 3、最后启动路由引擎
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
2.运行代码
$ go run .\main.go
3.访问测试
打开浏览器,输入http://127.0.0.1:8080/api/user/info/WithStruct,提示404报错信息,这是因为浏览器默认都是GET请求。
4.使用Postman工具进行GET请求测试
填写Uri内容:http://127.0.0.1:8080/api/user后,在【Params】下面定义下面的Key和Value后,url会变为http://127.0.0.1:8080/api/user/info?username=zq&age=18&address=北京&gen=man&id=1
| Key | Value |
|---|---|
| username | zq |
| age | 18 |
| address | 北京 |
| gen | man |
| ID | 1 |

点击【Send】,返回回显内容如下:
{
"address": "\"北京\"",
"age": "18",
"name": "zq"
}
控制台回显内容如下:
...
查询用户的ID是: 1
拿到了用户名: zq
拿到了地址: 北京
拿到了年龄: 18
...
十、Gin获取POST请求参数¶
10.1 form-data类型¶
1.编写代码文件
基于上面内容重新定义main.go文件,添加form-data类型的POST请求参数
package main
import (
"fmt"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
func getUserInfo(c *gin.Context) {
/*
{
"status": 200 //请求我已经收到了,处理也是正常的
"message": "获取成功", //msg
"data": {
"name": "zq",
"age": "18",
"address": "北京"
}
}
*/
// 接收请求参数
u := c.Query("username")
// address := c.Query("address")
// 配置参数的默认值
address := c.DefaultQuery("address", "北京")
a := c.Query("age")
id := c.Query("id")
idNumber, _ := strconv.Atoi(id)
fmt.Println("查询用户的ID是: ", idNumber)
// 去数据库中去查询用户信息
fmt.Println("拿到了用户名: ", u)
fmt.Println("拿到了地址: ", address)
fmt.Println("拿到了年龄: ", a)
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "查询成功"
userInfo := make(map[string]string)
userInfo["name"] = u
userInfo["age"] = a
userInfo["address"] = address
returnData["data"] = userInfo
c.JSON(http.StatusOK, userInfo)
}
/*
1.用户发来请求,查询用户信息username="xxx"
2.我们的后端是不是要根据用户发来请求,获取到他要查询哪个用户的信息
3.后端从数据库查询到xxx用户详细信息
4.查询数据绑定到结构体,gorm,orm
*/
type UserInfo struct {
// json:后面无空格
Username string `json:"username"`
Age int `json:"age"`
Address string `json:"address"`
}
// returnData
type ReturnData struct {
Status int
Message string
Data UserInfo `json:"data"`
}
// POST数据提交格式有两种:
// JSON()
// form-data
func addUser(c *gin.Context) {
// form-data使用
u := c.PostForm("username")
age, _ := strconv.Atoi(c.PostForm("age"))
// 配置POST请求的默认值
address := c.DefaultPostForm("address", "三亚")
userInfo := UserInfo{}
userInfo.Address = address
userInfo.Age = age
userInfo.Username = u
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "创建成功"
returnData["data"] = userInfo
c.JSON(http.StatusOK, returnData)
}
func addArticle(c *gin.Context) {
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "创建成功"
c.JSON(http.StatusOK, returnData)
}
// 使用结构体返回数据给前端
func returnDataWithStruct(c *gin.Context) {
userInfo := UserInfo{
Username: "zq",
Age: 18,
Address: "北京",
}
c.JSON(http.StatusOK, userInfo)
}
func main() {
// 1、首先创建一个路由引擎(方便之后可以在路由引擎中添加一些路由和路由的处理逻辑)
r := gin.Default()
// 2、其次添加路由至路由引擎,以及制定该路由的处理逻辑
// 2.1 添加路由路径
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
r.GET("/", func(c *gin.Context) {
// 状态码
c.String(http.StatusOK, "你在访问的是首页")
})
r.GET("/api/user/info", getUserInfo)
r.GET("/api/user/info/WithStruct", returnDataWithStruct)
r.POST("/api/user/add", addUser)
r.POST("/api/addArticle/add", addArticle)
// 3、最后启动路由引擎
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
2.运行代码
$ go run .\main.go
3.使用Postman工具进行POST请求测试
第一次测试(address使用默认值)
填写Uri内容:http://127.0.0.1:8080/api/user/add后,点击【Body】后,选择"form-data",定义下面的Key和Value后,点击【Send】
| Key | Value |
|---|---|
| username | zq |
| age | 18 |

回显内容为:
{
"data": {
"username": "zq",
"age": 18,
"address": "三亚"
},
"message": "创建成功",
"status": 200
}
第二次测试(address使用指定值)
填写Uri内容:http://127.0.0.1:8080/api/user/add后,点击【Body】后,选择"form-data",定义下面的Key和Value后,点击【Send】
| Key | Value |
|---|---|
| username | zq |
| age | 18 |
| address | 北京 |

回显内容为:
{
"data": {
"username": "zq",
"age": 18,
"address": "北京"
},
"message": "创建成功",
"status": 200
}
10.2 JSON类型¶
拿到json串之后,也有两种处理方式:
- 转成一个map
- 模型绑定:可以将请求的数据直接绑定到结构体
10.2.1 转成一个map¶
1.编写代码文件
基于上面内容重新定义main.go文件
package main
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
func getUserInfo(c *gin.Context) {
/*
{
"status": 200 //请求我已经收到了,处理也是正常的
"message": "获取成功", //msg
"data": {
"name": "zq",
"age": "18",
"address": "北京"
}
}
*/
// 接收请求参数
u := c.Query("username")
// address := c.Query("address")
// 配置参数的默认值
address := c.DefaultQuery("address", "北京")
a := c.Query("age")
id := c.Query("id")
idNumber, _ := strconv.Atoi(id)
fmt.Println("查询用户的ID是: ", idNumber)
// 去数据库中去查询用户信息
fmt.Println("拿到了用户名: ", u)
fmt.Println("拿到了地址: ", address)
fmt.Println("拿到了年龄: ", a)
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "查询成功"
userInfo := make(map[string]string)
userInfo["name"] = u
userInfo["age"] = a
userInfo["address"] = address
returnData["data"] = userInfo
c.JSON(http.StatusOK, userInfo)
}
/*
1.用户发来请求,查询用户信息username="xxx"
2.我们的后端是不是要根据用户发来请求,获取到他要查询哪个用户的信息
3.后端从数据库查询到xxx用户详细信息
4.查询数据绑定到结构体,gorm,orm
*/
type UserInfo struct {
// json:后面无空格
Username string `json:"username"`
Age int `json:"age"`
Address string `json:"address"`
}
// returnData
type ReturnData struct {
Status int
Message string
Data UserInfo `json:"data"`
}
// POST数据提交格式有两种:
// JSON()
// form-data
func addUser(c *gin.Context) {
// form-data使用
u := c.PostForm("username")
age, _ := strconv.Atoi(c.PostForm("age"))
// 配置POST请求的默认值
address := c.DefaultPostForm("address", "三亚")
userInfo := UserInfo{}
userInfo.Address = address
userInfo.Age = age
userInfo.Username = u
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "创建成功"
returnData["data"] = userInfo
c.JSON(http.StatusOK, returnData)
}
func addUserByJson(c *gin.Context) {
jsonData, _ := c.GetRawData()
// 拿到json串之后,也有两种处理
// 1.转成一个map
var m map[string]interface{}
json.Unmarshal(jsonData, &m)
c.JSON(http.StatusOK, m)
// 2.赋值给结构体
}
func addArticle(c *gin.Context) {
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "创建成功"
c.JSON(http.StatusOK, returnData)
}
// 使用结构体返回数据给前端
func returnDataWithStruct(c *gin.Context) {
userInfo := UserInfo{
Username: "zq",
Age: 18,
Address: "北京",
}
c.JSON(http.StatusOK, userInfo)
}
func main() {
// 1、首先创建一个路由引擎(方便之后可以在路由引擎中添加一些路由和路由的处理逻辑)
r := gin.Default()
// 2、其次添加路由至路由引擎,以及制定该路由的处理逻辑
// 2.1 添加路由路径
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
r.GET("/", func(c *gin.Context) {
// 状态码
c.String(http.StatusOK, "你在访问的是首页")
})
r.GET("/api/user/info", getUserInfo)
r.GET("/api/user/info/WithStruct", returnDataWithStruct)
r.POST("/api/user/add", addUser)
r.POST("/api/user/addUserByJson", addUserByJson)
r.POST("/api/addArticle/add", addArticle)
// 3、最后启动路由引擎
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
2.运行代码
$ go run .\main.go
3.使用Postman工具进行POST请求测试
填写Uri内容:http://127.0.0.1:8080/api/user/addUserByJson后,点击【Body】后,选择"raw",填写下面内容后,点击【Send】
{"username": "zq","age": 18}

回显内容
{
"age": 18,
"username": "zq"
}
10.2.2 模型绑定¶
1.编写代码文件
基于上面内容重新定义main.go文件,添加GET请求参数
package main
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
func getUserInfo(c *gin.Context) {
/*
{
"status": 200 //请求我已经收到了,处理也是正常的
"message": "获取成功", //msg
"data": {
"name": "zq",
"age": "18",
"address": "北京"
}
}
*/
// 接收请求参数
u := c.Query("username")
// address := c.Query("address")
// 配置参数的默认值
address := c.DefaultQuery("address", "北京")
a := c.Query("age")
id := c.Query("id")
idNumber, _ := strconv.Atoi(id)
fmt.Println("查询用户的ID是: ", idNumber)
// 去数据库中去查询用户信息
fmt.Println("拿到了用户名: ", u)
fmt.Println("拿到了地址: ", address)
fmt.Println("拿到了年龄: ", a)
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "查询成功"
userInfo := make(map[string]string)
userInfo["name"] = u
userInfo["age"] = a
userInfo["address"] = address
returnData["data"] = userInfo
c.JSON(http.StatusOK, userInfo)
}
/*
1.用户发来请求,查询用户信息username="xxx"
2.我们的后端是不是要根据用户发来请求,获取到他要查询哪个用户的信息
3.后端从数据库查询到xxx用户详细信息
4.查询数据绑定到结构体,gorm,orm
*/
type UserInfo struct {
// json:后面无空格
Username string `json:"username"`
Age int `json:"age"`
Address string `json:"address"`
}
// returnData
type ReturnData struct {
Status int
Message string
Data UserInfo `json:"data"`
}
// POST数据提交格式有两种:
// JSON()
// form-data
func addUser(c *gin.Context) {
// form-data使用
u := c.PostForm("username")
age, _ := strconv.Atoi(c.PostForm("age"))
// 配置POST请求的默认值
address := c.DefaultPostForm("address", "三亚")
userInfo := UserInfo{}
userInfo.Address = address
userInfo.Age = age
userInfo.Username = u
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "创建成功"
returnData["data"] = userInfo
c.JSON(http.StatusOK, returnData)
}
func addUserByJson(c *gin.Context) {
jsonData, _ := c.GetRawData()
// 拿到json串之后,也有两种处理
// 1.转成一个map
var m map[string]interface{}
json.Unmarshal(jsonData, &m)
c.JSON(http.StatusOK, m)
}
// 常用:模型绑定:可以将请求的数据直接绑定到结构体
func addUserBindStruct(c *gin.Context) {
userInfo := UserInfo{}
if err := c.ShouldBindJSON(&userInfo); err != nil {
// 说明绑定失败
fmt.Println("数据绑定失败:", err.Error())
c.JSON(http.StatusOK, gin.H{
"message": "参数格式不正确",
"status": 500,
})
} else {
c.JSON(http.StatusOK, userInfo)
}
}
func addArticle(c *gin.Context) {
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "创建成功"
c.JSON(http.StatusOK, returnData)
}
// 使用结构体返回数据给前端
func returnDataWithStruct(c *gin.Context) {
userInfo := UserInfo{
Username: "zq",
Age: 18,
Address: "北京",
}
c.JSON(http.StatusOK, userInfo)
}
func main() {
// 1、首先创建一个路由引擎(方便之后可以在路由引擎中添加一些路由和路由的处理逻辑)
r := gin.Default()
// 2、其次添加路由至路由引擎,以及制定该路由的处理逻辑
// 2.1 添加路由路径
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
r.GET("/", func(c *gin.Context) {
// 状态码
c.String(http.StatusOK, "你在访问的是首页")
})
r.GET("/api/user/info", getUserInfo)
r.GET("/api/user/info/WithStruct", returnDataWithStruct)
r.POST("/api/user/add", addUser)
r.POST("/api/user/addUserByJson", addUserByJson)
r.POST("/api/user/addUserBindStruct", addUserBindStruct)
r.POST("/api/addArticle/add", addArticle)
// 3、最后启动路由引擎
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
2.运行代码
$ go run .\main.go
3.使用Postman工具进行POST请求测试
第一次测试
填写Uri内容:http://127.0.0.1:8080/api/user/addUserBindStruct后,点击【Body】后,选择"raw",填写下面内容后,点击【Send】
{"username": "zq","age": 18}

回显内容
{
"username": "zq",
"age": 18,
"address": ""
}
第二次测试
填写Uri内容:http://127.0.0.1:8080/api/user/addUserBindStruct后,点击【Body】后,选择"raw",填写内容为空后,点击【Send】

回显内容
{
"message": "参数格式不正确",
"status": 500
}
十一、Gin路由分组¶
1.编写代码文件
基于上面内容重新定义main.go文件
添加如下代码
apiGroup := r.Group("/api")
productGroup := apiGroup.Group("/product")
productGroup.GET("/get", getUserInfo)
productGroup.POST("/add", addUser)
完整代码:
package main
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
func getUserInfo(c *gin.Context) {
/*
{
"status": 200 //请求我已经收到了,处理也是正常的
"message": "获取成功", //msg
"data": {
"name": "zq",
"age": "18",
"address": "北京"
}
}
*/
// 接收请求参数
u := c.Query("username")
// address := c.Query("address")
// 配置参数的默认值
address := c.DefaultQuery("address", "北京")
a := c.Query("age")
id := c.Query("id")
idNumber, _ := strconv.Atoi(id)
fmt.Println("查询用户的ID是: ", idNumber)
// 去数据库中去查询用户信息
fmt.Println("拿到了用户名: ", u)
fmt.Println("拿到了地址: ", address)
fmt.Println("拿到了年龄: ", a)
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "查询成功"
userInfo := make(map[string]string)
userInfo["name"] = u
userInfo["age"] = a
userInfo["address"] = address
returnData["data"] = userInfo
c.JSON(http.StatusOK, userInfo)
}
/*
1.用户发来请求,查询用户信息username="xxx"
2.我们的后端是不是要根据用户发来请求,获取到他要查询哪个用户的信息
3.后端从数据库查询到xxx用户详细信息
4.查询数据绑定到结构体,gorm,orm
*/
type UserInfo struct {
// json:后面无空格
Username string `json:"username"`
Age int `json:"age"`
Address string `json:"address"`
}
// returnData
type ReturnData struct {
Status int
Message string
Data UserInfo `json:"data"`
}
// POST数据提交格式有两种:
// JSON()
// form-data
func addUser(c *gin.Context) {
// form-data使用
u := c.PostForm("username")
age, _ := strconv.Atoi(c.PostForm("age"))
// 配置POST请求的默认值
address := c.DefaultPostForm("address", "三亚")
userInfo := UserInfo{}
userInfo.Address = address
userInfo.Age = age
userInfo.Username = u
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "创建成功"
returnData["data"] = userInfo
c.JSON(http.StatusOK, returnData)
}
func addUserByJson(c *gin.Context) {
jsonData, _ := c.GetRawData()
// 拿到json串之后,也有两种处理
// 1.转成一个map
var m map[string]interface{}
json.Unmarshal(jsonData, &m)
c.JSON(http.StatusOK, m)
}
// 常用:模型绑定:可以将请求的数据直接绑定到结构体
func addUserBindStruct(c *gin.Context) {
userInfo := UserInfo{}
if err := c.ShouldBindJSON(&userInfo); err != nil {
// 说明绑定失败
fmt.Println("数据绑定失败:", err.Error())
c.JSON(http.StatusOK, gin.H{
"message": "参数格式不正确",
"status": 500,
})
} else {
c.JSON(http.StatusOK, userInfo)
}
}
func addArticle(c *gin.Context) {
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "创建成功"
c.JSON(http.StatusOK, returnData)
}
// 使用结构体返回数据给前端
func returnDataWithStruct(c *gin.Context) {
userInfo := UserInfo{
Username: "zq",
Age: 18,
Address: "北京",
}
c.JSON(http.StatusOK, userInfo)
}
func main() {
// 1、首先创建一个路由引擎(方便之后可以在路由引擎中添加一些路由和路由的处理逻辑)
r := gin.Default()
// 2、其次添加路由至路由引擎,以及制定该路由的处理逻辑
// 2.1 添加路由路径
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
r.GET("/", func(c *gin.Context) {
// 状态码
c.String(http.StatusOK, "你在访问的是首页")
})
// 使用独立的函数处理路由逻辑
r.GET("/api/user/info", getUserInfo)
r.GET("/api/user/info/WithStruct", returnDataWithStruct)
r.POST("/api/user/add", addUser)
r.POST("/api/user/addUserByJson", addUserByJson)
r.POST("/api/user/addUserBindStruct", addUserBindStruct)
r.POST("/api/addArticle/add", addArticle)
/*
路由分组:
/v1/api/user
/add
/delete
/update
/v1/api/product
/add
/delete
/update
/v1/api/order
/add
/delete
/update
*/
apiGroup := r.Group("/api")
productGroup := apiGroup.Group("/product")
productGroup.GET("/get", getUserInfo)
productGroup.POST("/add", addUser)
// 3、最后启动路由引擎
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
2.运行代码
$ go run .\main.go
3.使用Postman工具进行GET请求测试
填写Uri内容:http://127.0.0.1:8080/api/product/get后,点击【Send】

Postman工具回显内容
{
"address": "北京",
"age": "",
"name": ""
}
4.使用Postman工具进行POST请求测试
填写Uri内容:http://127.0.0.1:8080/api/product/add后,点击【Send】

Postman工具回显内容
{
"data": {
"username": "",
"age": 0,
"address": "三亚"
},
"message": "创建成功",
"status": 200
}
十二、Gin中间件¶
12.1 Gin中间件初体验¶
在接收请求时,需要对一些请求做一些额外的处理,比如有没有权限执行操作。这些处理叫做钩子函数,也叫中间件
12.1.1 全局中间件¶
1.编写代码文件
基于上面内容重新定义main.go文件
添加如下代码
// 定义中间件
func globalMiddleWare(c *gin.Context) {
token := c.Query("token")
if token == "" {
// 没有带token,说明没有登录
c.String(401, "用户未登录")
c.Abort() //停止请求
}
fmt.Println("用户未登录")
}
// 中间件1:添加一个全局的中间件
// token
apiGroup.Use(globalMiddleWare)
完整代码:
package main
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
// 定义中间件
func globalMiddleWare(c *gin.Context) {
token := c.Query("token")
if token == "" {
// 没有带token,说明没有登录
c.String(401, "用户未登录")
c.Abort() //停止请求
}
fmt.Println("用户未登录")
}
func getUserInfo(c *gin.Context) {
/*
{
"status": 200 //请求我已经收到了,处理也是正常的
"message": "获取成功", //msg
"data": {
"name": "zq",
"age": "18",
"address": "北京"
}
}
*/
// 接收请求参数
u := c.Query("username")
// address := c.Query("address")
// 配置参数的默认值
address := c.DefaultQuery("address", "北京")
a := c.Query("age")
id := c.Query("id")
idNumber, _ := strconv.Atoi(id)
fmt.Println("查询用户的ID是: ", idNumber)
// 去数据库中去查询用户信息
fmt.Println("拿到了用户名: ", u)
fmt.Println("拿到了地址: ", address)
fmt.Println("拿到了年龄: ", a)
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "查询成功"
userInfo := make(map[string]string)
userInfo["name"] = u
userInfo["age"] = a
userInfo["address"] = address
returnData["data"] = userInfo
c.JSON(http.StatusOK, userInfo)
}
/*
1.用户发来请求,查询用户信息username="xxx"
2.我们的后端是不是要根据用户发来请求,获取到他要查询哪个用户的信息
3.后端从数据库查询到xxx用户详细信息
4.查询数据绑定到结构体,gorm,orm
*/
type UserInfo struct {
// json:后面无空格
Username string `json:"username"`
Age int `json:"age"`
Address string `json:"address"`
}
// returnData
type ReturnData struct {
Status int
Message string
Data UserInfo `json:"data"`
}
// POST数据提交格式有两种:
// JSON()
// form-data
func addUser(c *gin.Context) {
// form-data使用
u := c.PostForm("username")
age, _ := strconv.Atoi(c.PostForm("age"))
// 配置POST请求的默认值
address := c.DefaultPostForm("address", "三亚")
userInfo := UserInfo{}
userInfo.Address = address
userInfo.Age = age
userInfo.Username = u
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "创建成功"
returnData["data"] = userInfo
c.JSON(http.StatusOK, returnData)
}
func addUserByJson(c *gin.Context) {
jsonData, _ := c.GetRawData()
// 拿到json串之后,也有两种处理
// 1.转成一个map
var m map[string]interface{}
json.Unmarshal(jsonData, &m)
c.JSON(http.StatusOK, m)
}
// 常用:模型绑定:可以将请求的数据直接绑定到结构体
func addUserBindStruct(c *gin.Context) {
userInfo := UserInfo{}
if err := c.ShouldBindJSON(&userInfo); err != nil {
// 说明绑定失败
fmt.Println("数据绑定失败:", err.Error())
c.JSON(http.StatusOK, gin.H{
"message": "参数格式不正确",
"status": 500,
})
} else {
c.JSON(http.StatusOK, userInfo)
}
}
func addArticle(c *gin.Context) {
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "创建成功"
c.JSON(http.StatusOK, returnData)
}
// 使用结构体返回数据给前端
func returnDataWithStruct(c *gin.Context) {
userInfo := UserInfo{
Username: "zq",
Age: 18,
Address: "北京",
}
c.JSON(http.StatusOK, userInfo)
}
func main() {
// 1、首先创建一个路由引擎(方便之后可以在路由引擎中添加一些路由和路由的处理逻辑)
r := gin.Default()
// 2、其次添加路由至路由引擎,以及制定该路由的处理逻辑
// 2.1 添加路由路径
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
r.GET("/", func(c *gin.Context) {
// 状态码
c.String(http.StatusOK, "你在访问的是首页")
})
// 使用独立的函数处理路由逻辑
r.GET("/api/user/info", getUserInfo)
r.GET("/api/user/info/WithStruct", returnDataWithStruct)
r.POST("/api/user/add", addUser)
r.POST("/api/user/addUserByJson", addUserByJson)
r.POST("/api/user/addUserBindStruct", addUserBindStruct)
r.POST("/api/addArticle/add", addArticle)
/*
路由分组:
/v1/api/user
/add
/delete
/update
/v1/api/product
/add
/delete
/update
/v1/api/order
/add
/delete
/update
*/
/*
gin中间件:在接收请求时,需要对一些请求做一些额外的处理,比如有没有权限执行操作。这些处理叫做钩子函数,也叫中间件
*/
apiGroup := r.Group("/api")
// 中间件1:添加一个全局的中间件
// token
apiGroup.Use(globalMiddleWare)
productGroup := apiGroup.Group("/product")
productGroup.GET("/get", getUserInfo)
productGroup.POST("/add", addUser)
// 3、最后启动路由引擎
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
2.运行代码
$ go run .\main.go
3.使用Postman工具进行GET请求测试
填写Uri内容:http://127.0.0.1:8080/api/product/get后,点击【Send】后,返回401并输出提示信息"用户未登录"

12.1.2 局部中间件¶
1.编写代码文件
基于上面内容重新定义main.go文件
添加如下代码
// 定义局部中间件
func deleteMiddleWare(c *gin.Context) {
user := c.Query("username")
if user != "admin" {
c.String(403, "用户没有权限执行该操作")
c.Abort() //停止请求
}
}
func deleteProduct(c *gin.Context) {
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "删除成功"
c.JSON(http.StatusOK, returnData)
}
// 中间件2:添加一个局部的中间件
// 功能:当我们的username=admin的时候才能进行删除的操作
productGroup.GET("/delete", deleteMiddleWare, deleteProduct)
完整代码:
package main
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
// 定义全局中间件
func globalMiddleWare(c *gin.Context) {
token := c.Query("token")
if token == "" {
// 没有带token,说明没有登录
c.String(401, "用户未登录")
c.Abort() //停止请求
}
fmt.Println("用户未登录")
}
// 定义局部中间件
func deleteMiddleWare(c *gin.Context) {
user := c.Query("username")
if user != "admin" {
c.String(403, "用户没有权限执行该操作")
c.Abort() //停止请求
}
}
func deleteProduct(c *gin.Context) {
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "删除成功"
c.JSON(http.StatusOK, returnData)
}
func getUserInfo(c *gin.Context) {
/*
{
"status": 200 //请求我已经收到了,处理也是正常的
"message": "获取成功", //msg
"data": {
"name": "zq",
"age": "18",
"address": "北京"
}
}
*/
// 接收请求参数
u := c.Query("username")
// address := c.Query("address")
// 配置参数的默认值
address := c.DefaultQuery("address", "北京")
a := c.Query("age")
id := c.Query("id")
idNumber, _ := strconv.Atoi(id)
fmt.Println("查询用户的ID是: ", idNumber)
// 去数据库中去查询用户信息
fmt.Println("拿到了用户名: ", u)
fmt.Println("拿到了地址: ", address)
fmt.Println("拿到了年龄: ", a)
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "查询成功"
userInfo := make(map[string]string)
userInfo["name"] = u
userInfo["age"] = a
userInfo["address"] = address
returnData["data"] = userInfo
c.JSON(http.StatusOK, userInfo)
}
/*
1.用户发来请求,查询用户信息username="xxx"
2.我们的后端是不是要根据用户发来请求,获取到他要查询哪个用户的信息
3.后端从数据库查询到xxx用户详细信息
4.查询数据绑定到结构体,gorm,orm
*/
type UserInfo struct {
// json:后面无空格
Username string `json:"username"`
Age int `json:"age"`
Address string `json:"address"`
}
// returnData
type ReturnData struct {
Status int
Message string
Data UserInfo `json:"data"`
}
// POST数据提交格式有两种:
// JSON()
// form-data
func addUser(c *gin.Context) {
// form-data使用
u := c.PostForm("username")
age, _ := strconv.Atoi(c.PostForm("age"))
// 配置POST请求的默认值
address := c.DefaultPostForm("address", "三亚")
userInfo := UserInfo{}
userInfo.Address = address
userInfo.Age = age
userInfo.Username = u
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "创建成功"
returnData["data"] = userInfo
c.JSON(http.StatusOK, returnData)
}
func addUserByJson(c *gin.Context) {
jsonData, _ := c.GetRawData()
// 拿到json串之后,也有两种处理
// 1.转成一个map
var m map[string]interface{}
json.Unmarshal(jsonData, &m)
c.JSON(http.StatusOK, m)
}
// 常用:模型绑定:可以将请求的数据直接绑定到结构体
func addUserBindStruct(c *gin.Context) {
userInfo := UserInfo{}
if err := c.ShouldBindJSON(&userInfo); err != nil {
// 说明绑定失败
fmt.Println("数据绑定失败:", err.Error())
c.JSON(http.StatusOK, gin.H{
"message": "参数格式不正确",
"status": 500,
})
} else {
c.JSON(http.StatusOK, userInfo)
}
}
func addArticle(c *gin.Context) {
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "创建成功"
c.JSON(http.StatusOK, returnData)
}
// 使用结构体返回数据给前端
func returnDataWithStruct(c *gin.Context) {
userInfo := UserInfo{
Username: "zq",
Age: 18,
Address: "北京",
}
c.JSON(http.StatusOK, userInfo)
}
func main() {
// 1、首先创建一个路由引擎(方便之后可以在路由引擎中添加一些路由和路由的处理逻辑)
r := gin.Default()
// 2、其次添加路由至路由引擎,以及制定该路由的处理逻辑
// 2.1 添加路由路径
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
r.GET("/", func(c *gin.Context) {
// 状态码
c.String(http.StatusOK, "你在访问的是首页")
})
// 使用独立的函数处理路由逻辑
r.GET("/api/user/info", getUserInfo)
r.GET("/api/user/info/WithStruct", returnDataWithStruct)
r.POST("/api/user/add", addUser)
r.POST("/api/user/addUserByJson", addUserByJson)
r.POST("/api/user/addUserBindStruct", addUserBindStruct)
r.POST("/api/addArticle/add", addArticle)
/*
路由分组:
/v1/api/user
/add
/delete
/update
/v1/api/product
/add
/delete
/update
/v1/api/order
/add
/delete
/update
*/
/*
gin中间件:在接收请求时,需要对一些请求做一些额外的处理,比如有没有权限执行操作。这些处理叫做钩子函数,也叫中间件
*/
apiGroup := r.Group("/api")
// 中间件1:添加一个全局的中间件
// token
apiGroup.Use(globalMiddleWare)
productGroup := apiGroup.Group("/product")
productGroup.GET("/get", getUserInfo)
productGroup.POST("/add", addUser)
// 中间件2:添加一个局部的中间件
// 功能:当我们的username=admin的时候才能进行删除的操作
productGroup.GET("/delete", deleteMiddleWare, deleteProduct)
// 3、最后启动路由引擎
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
2.运行代码
$ go run .\main.go
3.使用Postman工具进行GET请求测试
第一次测试(非admin用户)
填写Uri内容:http://127.0.0.1:8080/api/product/delete?token=xxx&username=zq后,点击【Send】,Postman工具返回"用户没有权限执行该操作"消息
| Key | Value |
|---|---|
| token | xx |
| username | zq |

第二次测试(admin用户)
填写Uri内容:http://127.0.0.1:8080/api/product/delete?token=xxx&username=admin后,点击【Send】,返回内容如下:
{
"message": "删除成功",
"status": 200
}
| Key | Value |
|---|---|
| token | xx |
| username | admin |

12.1.3 多个中间件¶
1.编写代码文件
基于上面内容重新定义main.go文件
添加如下代码
// 定义多个中间件middleware1, middleware2, middleware3
func middleware1(c *gin.Context) {
fmt.Println("middleware1")
}
func middleware2(c *gin.Context) {
fmt.Println("middleware2")
}
func middleware3(c *gin.Context) {
fmt.Println("middleware3")
}
// 中间件3:配置多个中间件时,执行顺序采用先进先出的原则
productGroup.GET("/list", middleware1, middleware2, middleware3, getUserInfo)
完整代码文件:
package main
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
// 定义全局中间件
func globalMiddleWare(c *gin.Context) {
token := c.Query("token")
if token == "" {
// 没有带token,说明没有登录
c.String(401, "用户未登录")
c.Abort() //停止请求
}
fmt.Println("用户未登录")
}
// 定义局部中间件
func deleteMiddleWare(c *gin.Context) {
user := c.Query("username")
if user != "admin" {
c.String(403, "用户没有权限执行该操作")
c.Abort() //停止请求
}
}
func deleteProduct(c *gin.Context) {
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "删除成功"
c.JSON(http.StatusOK, returnData)
}
// 定义多个中间件middleware1, middleware2, middleware3
func middleware1(c *gin.Context) {
fmt.Println("middleware1")
}
func middleware2(c *gin.Context) {
fmt.Println("middleware2")
}
func middleware3(c *gin.Context) {
fmt.Println("middleware3")
}
func getUserInfo(c *gin.Context) {
/*
{
"status": 200 //请求我已经收到了,处理也是正常的
"message": "获取成功", //msg
"data": {
"name": "zq",
"age": "18",
"address": "北京"
}
}
*/
// 接收请求参数
u := c.Query("username")
// address := c.Query("address")
// 配置参数的默认值
address := c.DefaultQuery("address", "北京")
a := c.Query("age")
id := c.Query("id")
idNumber, _ := strconv.Atoi(id)
fmt.Println("查询用户的ID是: ", idNumber)
// 去数据库中去查询用户信息
fmt.Println("拿到了用户名: ", u)
fmt.Println("拿到了地址: ", address)
fmt.Println("拿到了年龄: ", a)
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "查询成功"
userInfo := make(map[string]string)
userInfo["name"] = u
userInfo["age"] = a
userInfo["address"] = address
returnData["data"] = userInfo
c.JSON(http.StatusOK, userInfo)
}
/*
1.用户发来请求,查询用户信息username="xxx"
2.我们的后端是不是要根据用户发来请求,获取到他要查询哪个用户的信息
3.后端从数据库查询到xxx用户详细信息
4.查询数据绑定到结构体,gorm,orm
*/
type UserInfo struct {
// json:后面无空格
Username string `json:"username"`
Age int `json:"age"`
Address string `json:"address"`
}
// returnData
type ReturnData struct {
Status int
Message string
Data UserInfo `json:"data"`
}
// POST数据提交格式有两种:
// JSON()
// form-data
func addUser(c *gin.Context) {
// form-data使用
u := c.PostForm("username")
age, _ := strconv.Atoi(c.PostForm("age"))
// 配置POST请求的默认值
address := c.DefaultPostForm("address", "三亚")
userInfo := UserInfo{}
userInfo.Address = address
userInfo.Age = age
userInfo.Username = u
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "创建成功"
returnData["data"] = userInfo
c.JSON(http.StatusOK, returnData)
}
func addUserByJson(c *gin.Context) {
jsonData, _ := c.GetRawData()
// 拿到json串之后,也有两种处理
// 1.转成一个map
var m map[string]interface{}
json.Unmarshal(jsonData, &m)
c.JSON(http.StatusOK, m)
}
// 常用:模型绑定:可以将请求的数据直接绑定到结构体
func addUserBindStruct(c *gin.Context) {
userInfo := UserInfo{}
if err := c.ShouldBindJSON(&userInfo); err != nil {
// 说明绑定失败
fmt.Println("数据绑定失败:", err.Error())
c.JSON(http.StatusOK, gin.H{
"message": "参数格式不正确",
"status": 500,
})
} else {
c.JSON(http.StatusOK, userInfo)
}
}
func addArticle(c *gin.Context) {
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "创建成功"
c.JSON(http.StatusOK, returnData)
}
// 使用结构体返回数据给前端
func returnDataWithStruct(c *gin.Context) {
userInfo := UserInfo{
Username: "zq",
Age: 18,
Address: "北京",
}
c.JSON(http.StatusOK, userInfo)
}
func main() {
// 1、首先创建一个路由引擎(方便之后可以在路由引擎中添加一些路由和路由的处理逻辑)
r := gin.Default()
// 2、其次添加路由至路由引擎,以及制定该路由的处理逻辑
// 2.1 添加路由路径
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
r.GET("/", func(c *gin.Context) {
// 状态码
c.String(http.StatusOK, "你在访问的是首页")
})
// 使用独立的函数处理路由逻辑
r.GET("/api/user/info", getUserInfo)
r.GET("/api/user/info/WithStruct", returnDataWithStruct)
r.POST("/api/user/add", addUser)
r.POST("/api/user/addUserByJson", addUserByJson)
r.POST("/api/user/addUserBindStruct", addUserBindStruct)
r.POST("/api/addArticle/add", addArticle)
/*
路由分组:
/v1/api/user
/add
/delete
/update
/v1/api/product
/add
/delete
/update
/v1/api/order
/add
/delete
/update
*/
/*
gin中间件:在接收请求时,需要对一些请求做一些额外的处理,比如有没有权限执行操作。这些处理叫做钩子函数,也叫中间件
*/
apiGroup := r.Group("/api")
// 中间件1:添加一个全局的中间件
// token
apiGroup.Use(globalMiddleWare)
productGroup := apiGroup.Group("/product")
productGroup.GET("/get", getUserInfo)
productGroup.POST("/add", addUser)
// 中间件2:添加一个局部的中间件
// 功能:当我们的username=admin的时候才能进行删除的操作
productGroup.GET("/delete", deleteMiddleWare, deleteProduct)
// 中间件3:配置多个中间件时,执行顺序采用先进先出的原则
productGroup.GET("/list", middleware1, middleware2, middleware3, getUserInfo)
// 3、最后启动路由引擎
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
2.运行代码
$ go run .\main.go
3.使用Postman工具进行GET请求测试
填写Uri内容:http://127.0.0.1:8080/api/product/list?token=xxx后,点击【Send】,Postman工具返回内容如下:
{
"address": "北京",
"age": "",
"name": "admin"
}
| Key | Value |
|---|---|
| token | xx |

命令行界面回显内容如下,证实了如果存在多个中间件,遵循"先进先出"的规则
...
middleware1
middleware2
middleware3
...
12.2 Gin中间件Next和Abort方法¶
12.2.1 Next方法¶
你可以把Next方法想象成接力比赛中的 “交接棒” 动作。当一个中间件调用Next时,就相当于把处理请求的 “接力棒” 交给后续的中间件或者路由处理函数。后续的中间件和处理函数执行完毕后,还会回到当前的中间件,继续执行Next之后的代码。
1.编写代码文件
基于上面内容重新定义main.go文件
修改如下代码
func middleware1(c *gin.Context) {
fmt.Println("middleware1")
c.Next()
fmt.Println("middleware1 next")
}
func middleware2(c *gin.Context) {
fmt.Println("middleware2")
c.Next()
fmt.Println("middleware2 next")
}
完整代码文件:
package main
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
// 定义全局中间件
func globalMiddleWare(c *gin.Context) {
token := c.Query("token")
if token == "" {
// 没有带token,说明没有登录
c.String(401, "用户未登录")
c.Abort() //停止请求
}
fmt.Println("用户未登录")
}
// 定义局部中间件
func deleteMiddleWare(c *gin.Context) {
user := c.Query("username")
if user != "admin" {
c.String(403, "用户没有权限执行该操作")
c.Abort() //停止请求
}
}
func deleteProduct(c *gin.Context) {
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "删除成功"
c.JSON(http.StatusOK, returnData)
}
// 定义多个中间件middleware1, middleware2, middleware3
func middleware1(c *gin.Context) {
fmt.Println("middleware1")
c.Next()
fmt.Println("middleware1 next")
}
func middleware2(c *gin.Context) {
fmt.Println("middleware2")
c.Next()
fmt.Println("middleware2 next")
}
func middleware3(c *gin.Context) {
fmt.Println("middleware3")
}
func getUserInfo(c *gin.Context) {
/*
{
"status": 200 //请求我已经收到了,处理也是正常的
"message": "获取成功", //msg
"data": {
"name": "zq",
"age": "18",
"address": "北京"
}
}
*/
// 接收请求参数
u := c.Query("username")
// address := c.Query("address")
// 配置参数的默认值
address := c.DefaultQuery("address", "北京")
a := c.Query("age")
id := c.Query("id")
idNumber, _ := strconv.Atoi(id)
fmt.Println("查询用户的ID是: ", idNumber)
// 去数据库中去查询用户信息
fmt.Println("拿到了用户名: ", u)
fmt.Println("拿到了地址: ", address)
fmt.Println("拿到了年龄: ", a)
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "查询成功"
userInfo := make(map[string]string)
userInfo["name"] = u
userInfo["age"] = a
userInfo["address"] = address
returnData["data"] = userInfo
c.JSON(http.StatusOK, userInfo)
}
/*
1.用户发来请求,查询用户信息username="xxx"
2.我们的后端是不是要根据用户发来请求,获取到他要查询哪个用户的信息
3.后端从数据库查询到xxx用户详细信息
4.查询数据绑定到结构体,gorm,orm
*/
type UserInfo struct {
// json:后面无空格
Username string `json:"username"`
Age int `json:"age"`
Address string `json:"address"`
}
// returnData
type ReturnData struct {
Status int
Message string
Data UserInfo `json:"data"`
}
// POST数据提交格式有两种:
// JSON()
// form-data
func addUser(c *gin.Context) {
// form-data使用
u := c.PostForm("username")
age, _ := strconv.Atoi(c.PostForm("age"))
// 配置POST请求的默认值
address := c.DefaultPostForm("address", "三亚")
userInfo := UserInfo{}
userInfo.Address = address
userInfo.Age = age
userInfo.Username = u
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "创建成功"
returnData["data"] = userInfo
c.JSON(http.StatusOK, returnData)
}
func addUserByJson(c *gin.Context) {
jsonData, _ := c.GetRawData()
// 拿到json串之后,也有两种处理
// 1.转成一个map
var m map[string]interface{}
json.Unmarshal(jsonData, &m)
c.JSON(http.StatusOK, m)
}
// 常用:模型绑定:可以将请求的数据直接绑定到结构体
func addUserBindStruct(c *gin.Context) {
userInfo := UserInfo{}
if err := c.ShouldBindJSON(&userInfo); err != nil {
// 说明绑定失败
fmt.Println("数据绑定失败:", err.Error())
c.JSON(http.StatusOK, gin.H{
"message": "参数格式不正确",
"status": 500,
})
} else {
c.JSON(http.StatusOK, userInfo)
}
}
func addArticle(c *gin.Context) {
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "创建成功"
c.JSON(http.StatusOK, returnData)
}
// 使用结构体返回数据给前端
func returnDataWithStruct(c *gin.Context) {
userInfo := UserInfo{
Username: "zq",
Age: 18,
Address: "北京",
}
c.JSON(http.StatusOK, userInfo)
}
func main() {
// 1、首先创建一个路由引擎(方便之后可以在路由引擎中添加一些路由和路由的处理逻辑)
r := gin.Default()
// 2、其次添加路由至路由引擎,以及制定该路由的处理逻辑
// 2.1 添加路由路径
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
r.GET("/", func(c *gin.Context) {
// 状态码
c.String(http.StatusOK, "你在访问的是首页")
})
// 使用独立的函数处理路由逻辑
r.GET("/api/user/info", getUserInfo)
r.GET("/api/user/info/WithStruct", returnDataWithStruct)
r.POST("/api/user/add", addUser)
r.POST("/api/user/addUserByJson", addUserByJson)
r.POST("/api/user/addUserBindStruct", addUserBindStruct)
r.POST("/api/addArticle/add", addArticle)
/*
路由分组:
/v1/api/user
/add
/delete
/update
/v1/api/product
/add
/delete
/update
/v1/api/order
/add
/delete
/update
*/
/*
gin中间件:在接收请求时,需要对一些请求做一些额外的处理,比如有没有权限执行操作。这些处理叫做钩子函数,也叫中间件
*/
apiGroup := r.Group("/api")
// 中间件1:添加一个全局的中间件
// token
apiGroup.Use(globalMiddleWare)
productGroup := apiGroup.Group("/product")
productGroup.GET("/get", getUserInfo)
productGroup.POST("/add", addUser)
// 中间件2:添加一个局部的中间件
// 功能:当我们的username=admin的时候才能进行删除的操作
productGroup.GET("/delete", deleteMiddleWare, deleteProduct)
// 中间件3:配置多个中间件时,执行顺序采用先进先出的原则
productGroup.GET("/list", middleware1, middleware2, middleware3, getUserInfo)
// 3、最后启动路由引擎
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
2.运行代码
$ go run .\main.go
3.使用Postman工具进行GET请求测试
填写Uri内容:http://127.0.0.1:8080/api/product/list?token=xxx后,点击【Send】,Postman工具返回内容如下:
{
"address": "北京",
"age": "",
"name": "admin"
}
| Key | Value |
|---|---|
| token | xx |

命令行界面回显内容如下,观察到先打印middleware2 next再打印middleware1 next,遵循"先进后出"的规则
...
middleware2 next
middleware1 next
...
12.2.2 Abort方法¶
Abort方法的作用类似于 “紧急刹车”。一旦在中间件里调用了Abort,Gin 就会马上停止执行后续的中间件和路由处理函数,请求的处理流程会就此终止。不过,已经执行过的中间件中Next之后的代码还是会正常执行。
1.编写代码文件
基于上面内容重新定义main.go文件
修改如下代码
func middleware1(c *gin.Context) {
fmt.Println("middleware1")
// c.Next()
c.Abort()
fmt.Println("middleware1 next")
}
func middleware2(c *gin.Context) {
fmt.Println("middleware2")
// c.Next()
fmt.Println("middleware2 next")
}
完整代码文件:
package main
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
// 定义全局中间件
func globalMiddleWare(c *gin.Context) {
token := c.Query("token")
if token == "" {
// 没有带token,说明没有登录
c.String(401, "用户未登录")
c.Abort() //停止请求
}
fmt.Println("用户未登录")
}
// 定义局部中间件
func deleteMiddleWare(c *gin.Context) {
user := c.Query("username")
if user != "admin" {
c.String(403, "用户没有权限执行该操作")
c.Abort() //停止请求
}
}
func deleteProduct(c *gin.Context) {
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "删除成功"
c.JSON(http.StatusOK, returnData)
}
// 定义多个中间件middleware1, middleware2, middleware3
func middleware1(c *gin.Context) {
fmt.Println("middleware1")
// c.Next()
c.Abort()
fmt.Println("middleware1 next")
}
func middleware2(c *gin.Context) {
fmt.Println("middleware2")
// c.Next()
fmt.Println("middleware2 next")
}
func middleware3(c *gin.Context) {
fmt.Println("middleware3")
}
func getUserInfo(c *gin.Context) {
/*
{
"status": 200 //请求我已经收到了,处理也是正常的
"message": "获取成功", //msg
"data": {
"name": "zq",
"age": "18",
"address": "北京"
}
}
*/
// 接收请求参数
u := c.Query("username")
// address := c.Query("address")
// 配置参数的默认值
address := c.DefaultQuery("address", "北京")
a := c.Query("age")
id := c.Query("id")
idNumber, _ := strconv.Atoi(id)
fmt.Println("查询用户的ID是: ", idNumber)
// 去数据库中去查询用户信息
fmt.Println("拿到了用户名: ", u)
fmt.Println("拿到了地址: ", address)
fmt.Println("拿到了年龄: ", a)
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "查询成功"
userInfo := make(map[string]string)
userInfo["name"] = u
userInfo["age"] = a
userInfo["address"] = address
returnData["data"] = userInfo
c.JSON(http.StatusOK, userInfo)
}
/*
1.用户发来请求,查询用户信息username="xxx"
2.我们的后端是不是要根据用户发来请求,获取到他要查询哪个用户的信息
3.后端从数据库查询到xxx用户详细信息
4.查询数据绑定到结构体,gorm,orm
*/
type UserInfo struct {
// json:后面无空格
Username string `json:"username"`
Age int `json:"age"`
Address string `json:"address"`
}
// returnData
type ReturnData struct {
Status int
Message string
Data UserInfo `json:"data"`
}
// POST数据提交格式有两种:
// JSON()
// form-data
func addUser(c *gin.Context) {
// form-data使用
u := c.PostForm("username")
age, _ := strconv.Atoi(c.PostForm("age"))
// 配置POST请求的默认值
address := c.DefaultPostForm("address", "三亚")
userInfo := UserInfo{}
userInfo.Address = address
userInfo.Age = age
userInfo.Username = u
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "创建成功"
returnData["data"] = userInfo
c.JSON(http.StatusOK, returnData)
}
func addUserByJson(c *gin.Context) {
jsonData, _ := c.GetRawData()
// 拿到json串之后,也有两种处理
// 1.转成一个map
var m map[string]interface{}
json.Unmarshal(jsonData, &m)
c.JSON(http.StatusOK, m)
}
// 常用:模型绑定:可以将请求的数据直接绑定到结构体
func addUserBindStruct(c *gin.Context) {
userInfo := UserInfo{}
if err := c.ShouldBindJSON(&userInfo); err != nil {
// 说明绑定失败
fmt.Println("数据绑定失败:", err.Error())
c.JSON(http.StatusOK, gin.H{
"message": "参数格式不正确",
"status": 500,
})
} else {
c.JSON(http.StatusOK, userInfo)
}
}
func addArticle(c *gin.Context) {
returnData := make(map[string]interface{})
returnData["status"] = 200
returnData["message"] = "创建成功"
c.JSON(http.StatusOK, returnData)
}
// 使用结构体返回数据给前端
func returnDataWithStruct(c *gin.Context) {
userInfo := UserInfo{
Username: "zq",
Age: 18,
Address: "北京",
}
c.JSON(http.StatusOK, userInfo)
}
func main() {
// 1、首先创建一个路由引擎(方便之后可以在路由引擎中添加一些路由和路由的处理逻辑)
r := gin.Default()
// 2、其次添加路由至路由引擎,以及制定该路由的处理逻辑
// 2.1 添加路由路径
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
r.GET("/", func(c *gin.Context) {
// 状态码
c.String(http.StatusOK, "你在访问的是首页")
})
// 使用独立的函数处理路由逻辑
r.GET("/api/user/info", getUserInfo)
r.GET("/api/user/info/WithStruct", returnDataWithStruct)
r.POST("/api/user/add", addUser)
r.POST("/api/user/addUserByJson", addUserByJson)
r.POST("/api/user/addUserBindStruct", addUserBindStruct)
r.POST("/api/addArticle/add", addArticle)
/*
路由分组:
/v1/api/user
/add
/delete
/update
/v1/api/product
/add
/delete
/update
/v1/api/order
/add
/delete
/update
*/
/*
gin中间件:在接收请求时,需要对一些请求做一些额外的处理,比如有没有权限执行操作。这些处理叫做钩子函数,也叫中间件
*/
apiGroup := r.Group("/api")
// 中间件1:添加一个全局的中间件
// token
apiGroup.Use(globalMiddleWare)
productGroup := apiGroup.Group("/product")
productGroup.GET("/get", getUserInfo)
productGroup.POST("/add", addUser)
// 中间件2:添加一个局部的中间件
// 功能:当我们的username=admin的时候才能进行删除的操作
productGroup.GET("/delete", deleteMiddleWare, deleteProduct)
// 中间件3:配置多个中间件时,执行顺序采用先进先出的原则
productGroup.GET("/list", middleware1, middleware2, middleware3, getUserInfo)
// 3、最后启动路由引擎
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
2.运行代码
$ go run .\main.go
3.使用Postman工具进行GET请求测试
填写Uri内容:http://127.0.0.1:8080/api/product/list?token=xxx后,点击【Send】,Postman工具返回内容如下:
{
"address": "北京",
"age": "",
"name": "admin"
}
| Key | Value |
|---|---|
| token | xx |

命令行界面回显内容如下,观察到打印完middleware1和middleware1 next
后面的内容不再进行打印
...
middleware1
middleware1 next
...
十一、项目工程化¶
11.1 什么是项目工程化¶
项目工程化就是将代码根据不同的功能和环节,进行规范化、标准化、系统化及模块化,从而提高开发效率、降低开发成本、提高软件质量和可维护性。
11.2 如何实现项目工程化¶
实现项目工程化步骤:
- 导入必须的依赖包
- 加载程序配置
- 配置程序的路由规则
- 实现路由的处理逻辑
- 配置路由的中间件
- 数据的增删改查操作
- 工具类封装
项目工程化规范:
- config:配置层
- routers:路由层
- controllers:控制器层
- middlewares:中间件层
- models:模型层
- utils:工具类层
11.3 项目工程化实践¶
1、新建一个名为"project-demo"的文件夹并通过VScode软件打开
2、在project-demo文件夹下面执行go mod init project-demo初始化,生成对应的go.mod文件
$ go mod init project-demo
3、在project-demo文件夹下面新建下面文件夹,并在对应的文件夹下面新建同名的go文件
- config
- config.go
- routers
- routers.go
- controllers
- controllers.go
- middlewares
- middlewares.go
- models
- models.go
- utils
- utils.go
4、简单定义上述中的go文件
config.go文件
// 存放程序的配置信息
package config
import "fmt"
func init() {
fmt.Println("开始加载程序配置")
}
routers.go文件
// 路由层 配置程序的路由规则
package routers
controllers.go文件
// 控制器层
package controllers
middlewares.go文件
// 实现路由的处理逻辑
package middlewares
models.go文件
// 模型层
package models
utils.go文件
// 工具层
package utils
5、执行命令安装gin
$ go get github.com/gin-gonic/gin
6、在project-demo文件夹下面新建main.go文件并定义如下内容信息
// 项目的总入口
package main
import (
_ "project-demo/config"
"github.com/gin-gonic/gin"
)
func main() {
// 1.加载程序的配置
// 2.配置gin
r := gin.Default()
r.Run()
}
7、新建README.md文件,设置一些内容用来描述项目的信息
## xxx
8、运行代码
$ go run .\main.go
观察回显内容,观察到已经成功打印出"开始加载程序配置"信息
...
开始加载程序配置
...