目录
☀️前言
1.什么是前端?什么是后端?🌀
2.Gin框架介绍 🌷
3.Gin框架的基本使用 -Hello,World例子🌷
🌿入门示例 - Hello,World
💻补充(一些常用的网络术语):
3.1 Get请求与Post请求📞
3.2 状态码 🔧
4.Gin的各类返回方式 🌷
✏️返回字符串:
✏️返回xml:
✏️返回文件(如Excel,txt,png等文件):
✏️页面重定向:
5.如何处理前端的请求数据🌷
6.Gin框架实战案例 -表单信息提交处理🌷
4.1 前端JS请求 🔅
4.2 后端Gin编写 🔅
☀️前言
通过前面的基本学习,终于开始进入项目学习阶段。本文将主要介绍Go语言官方的后端框架Gin如何与前端数据交互,以及实现最基本的表单数据交互案例,而关于后端操作数据库的部分,将留在下一章进行详细讲解。
1.什么是前端?什么是后端?🌀
前端和后端是软件开发中的两个重要部分,它们共同协作完成一个完整的产品或系统。通俗来说,前端就像餐厅的前台,负责接待顾客、展示菜单、处理顾客的点餐需求,后端就像餐厅的后厨,负责准备食材、烹饪菜肴、确保菜品按时送到前台,前端是用户看到的“表面”,负责展示和交互。后端是用户看不到的“幕后”,负责处理数据和逻辑。
对比维度 | 前端 | 后端 |
---|---|---|
定义 | 前端是用户直接看到和交互的部分,主要负责展示界面和处理用户操作。 | 后端是运行在服务器上的部分,主要负责数据处理、逻辑运算和存储。 |
主要功能 | 展示页面、交互设计、响应用户操作(如点击按钮、输入内容)。 | 处理业务逻辑、管理数据库、提供 API 接口给前端调用。 |
技术栈 | HTML、CSS、JavaScript、框架(如 React、Vue、Angular)。 | 服务器语言(如 Python、Java、Node.js、PHP)、 数据库(如 MySQL、MongoDB)。 |
工作内容 | 设计和实现用户界面、优化用户体验、与后端 API 交互。 | 开发服务器端逻辑、管理数据库、处理数据、提供安全性和性能优化。 |
用户体验 | 直接影响用户对产品的第一印象,注重界面美观和交互流畅性。 | 间接影响用户体验,主要通过提供稳定、快速的服务来支持前端。 |
安全性 | 前端代码是公开的,容易被用户查看和篡改,安全性较弱。 | 后端代码运行在服务器上,用户无法直接访问,安全性较高。 |
开发工具 | 浏览器、代码编辑器(如 VS Code)、前端框架工具。 | 服务器环境、数据库管理工具、API 测试工具(如 Postman)。 |
2.Gin框架介绍 🌷
Gin 是一个用 Go 语言编写的高性能 HTTP Web 框架,以轻量级、快速路由和中间件支持著称。Gin的设计目标是帮助开发者快速构建Web应用和RESTful API,同时保持高性能和低资源占用。其主要特点如下:
- 高性能:基于Radix树的路由实现,内存占用小,无反射,API性能可预测。
- 零分配路由器:路由处理过程中不产生内存分配,确保高效。
- 中间件支持:支持链式中间件,可以用于日志记录、认证、GZIP压缩等。
- 崩溃恢复:能够捕获HTTP请求中的panic并恢复,确保服务器始终可用。
- JSON验证:支持对请求中的JSON数据进行验证,确保数据完整性。
- 路由分组:支持路由分组,便于组织不同功能的API版本或权限。
- 错误管理:提供方便的方式收集和处理HTTP请求中的错误。
- 内置渲染:支持JSON、XML和HTML的渲染。
- 可扩展性:易于创建自定义中间件,满足特定需求
同样Go语言的后端框架不止Gin一种,还有其它后端框架,如Beggo,比较结果如下:
框架 | 性能 | 路由 | 中间件 | 适用场景 | 学习曲线 |
---|---|---|---|---|---|
Gin | 极高 | 基于httprouter | 丰富且灵活 | API服务、微服务、高性能后端 | 低 |
Echo | 高 | 自定义实现 | 简洁 | RESTful API、轻量级应用 | 低 |
Fiber | 极高 | 类似Express | Express风格 | 替代Gin,追求极简语法 | 中 |
Beego | 中等 | 自带MVC路由 | 全功能 | 全栈开发(含ORM、模板引擎) | 中高 |
3.Gin框架的基本使用 -Hello,World例子🌷
首先我们得先导入Gin的包,熟悉python语言的同学都知道,对于 python 我们可以在终端使用 pip install 命令进行下载所需要的包,而go语言,可以在终端使用 go get 命令来获取第三方包,导入到项目中。我们在项目的终端中,输入如下命令,即可将Gin框架导入到项目中,接下来就可以使用Gin框架进行开发了。
$ go get -u github.com/gin-gonic/gin
🌿入门示例 - Hello,World
package mainimport "github.com/gin-gonic/gin" // 导入Gin的包func main() {r := gin.Default()r.GET("/", func(c *gin.Context) {c.JSON(200, gin.H{"message": "Hello, World!"})})r.Run(":8080") // 监听 0.0.0.0:8080
}
🌳解释🌳:
- r := gin.Default() 使用gin创建了一个默认的路由器实例,它会自动加载一些中间件,比如日志记录和恢复中间件,接下来由它负责处理网页的HTTP请求并调用相应的处理函数。
- r.get用于定义一个GET请求的路由。这里定义了一个路径为
"/"
的GET请求处理函数。当客户端访问应用的根路径"/"
时,会调用这个处理函数。c
是gin.Context
类型的参数,它包含了请求和响应的所有信息。c.JSON
用于向客户端发送JSON格式的响应。200
是HTTP状态码,表示请求成功。gin.H
是一个快捷方式,用于创建一个map[string]interface{}
,这里用于构建JSON响应内容。
r.Run()
启动HTTP服务器并开始监听请求。":8080"
指定了服务器监听的端口号为8080
通过本次简单的例子,我们可以归纳出更为普遍的Gin后端框架处理流程,如下图所示:
💻补充(一些常用的网络术语):
3.1 Get请求与Post请求📞
通俗来说,GET请求就像去图书馆借书,你告诉图书馆员你想借的书名(参数),这个书名会显示在借书单上(URL),别人可以看到。POST请求就像去图书馆还书,你把书交给图书馆员(数据放在请求体中),这个过程是相对私密的,别人不会知道你具体还了什么书。
对比维度 | GET请求 | POST请求 |
---|---|---|
用途 | 用于从服务器获取数据,比如访问一个网页或查询信息。 | 用于向服务器提交数据,比如提交表单或上传文件。 |
数据传递方式 | 参数附加在URL后面,比如http://example.com/search?q=keyword 。 | 数据放在请求体中,不会显示在URL中。 |
安全性 | 相对不安全,因为参数会显示在浏览器地址栏中,可能会被保存在浏览器历史记录或服务器日志中。 | 相对更安全,因为数据不会显示在浏览器地址栏中,也不容易被缓存或记录在历史中。 |
限制 | URL长度有限制,通常不能超过2048个字符,不适合传递大量数据。 | 没有长度限制,可以传递大量数据。 |
缓存 | 可以被浏览器缓存,多次访问相同的URL时可能不会每次都向服务器发送请求。 | 通常不会被浏览器缓存,每次提交都会向服务器发送请求。 |
幂等性 | 是幂等的,多次请求相同URL不会产生不同结果。 | 不是幂等的,多次提交相同数据可能会产生不同结果(如多次提交表单)。 |
书签 | 可以通过书签保存URL,方便后续访问。 | 不能通过书签保存请求体中的数据。 |
适用场景 | 适合简单的查询和数据获取,比如搜索、查看页面内容。 | 适合复杂操作和敏感数据的提交,比如登录、文件上传、表单提交等。 |
3.2 状态码 🔧
HTTP状态码是服务器对客户端请求的响应状态的三位数字代码,用于表示请求的处理结果。状态码分为以下几类:
-
1xx(信息性状态码):表示请求已接收,正在处理。
-
2xx(成功状态码):表示请求已成功处理。
-
3xx(重定向状态码):表示需要进一步操作才能完成请求。
-
4xx(客户端错误状态码):表示客户端请求有误,服务器无法处理。
-
5xx(服务器错误状态码):表示服务器处理请求时发生错误。
状态码 | 描述 |
---|---|
100 | 继续,表示服务器已收到请求的初始部分,客户端可以继续发送其余请求。 |
200 | 请求成功,服务器返回了请求的数据。 |
201 | 资源已成功创建。 |
301 | 资源的URL已永久更改。 |
302 | 资源的URL暂时更改。 |
400 | 请求有误,服务器无法处理。 |
401 | 请求未授权。 |
403 | 请求被拒绝。 |
404 | 请求的资源不存在。 |
500 | 服务器内部错误,无法完成请求。 |
502 | 服务器从上游服务器接收到的响应无效。 |
503 | 服务不可用,通常是服务器过载或维护 |
而在Go语言中,可以使用net/http包来处理HTTP请求和响应,如http.StatusOK代表状态码200,http.StatusBadRequest代表状态码400,http.StatusInternalServerError代表状态码500
4.Gin的各类返回方式 🌷
上述基本案例中,Gin返回的数据类型为json格式,下面介绍一些别的格式的数据返回。
✏️返回字符串:
router.GET("/str", func(c *gin.Context) {c.String(http.StatusOK, "返回成功")
})
✏️返回xml:
router.GET("/xml", func(c *gin.Context) {c.XML(http.StatusOK, gin.H{"user": "FJNU", "message": "hey", "status": http.StatusOK})
})
✏️返回文件(如Excel,txt,png等文件):
router.GET("/download/:filename", func(c *gin.Context) {filename := c.Param("filename") // 获取前端传输的文件名filePath := "./files/" + filename // 获取本地的文件路径c.File(filePath) // 将对应的文件传回前端})
上述方法用于返回单个文件,filename必须包含文件的扩展名(如.png),Gin会自动根据文件扩展名设置正确的MIME类型。
如果希望文件被浏览器作为附件下载而不是直接显示,可以使用c.FileAttachment()
方法。
package mainimport "github.com/gin-gonic/gin"func main() {router := gin.Default()router.GET("/download/:filename", func(c *gin.Context) {filename := c.Param("filename")filePath := "./files/" + filenamec.FileAttachment(filePath, filename)})router.Run(":8080")
}
✏️页面重定向:
如果你希望点击某个按钮后,跳转到另一个页面,可以使用以下方法:
router.GET("/redirect", func(c *gin.Context) {//支持内部和外部的重定向c.Redirect(http.StatusMovedPermanently, "http://www.baidu.com/")
})
5.如何处理前端的请求数据🌷
c *gin.Context 是 Gin 框架处理 HTTP 请求的核心对象,封装了请求和响应的所有操作。以下是其核心功能及用法:
功能 | 方法/属性 | 说明 |
---|---|---|
获取请求参数 | c.Param("key") | 获取路由参数(如 /user/:id → id ) |
c.Query("key") | 获取 URL 查询参数(如 ?name=John ) | |
c.PostForm("key") | 获取表单数据(Content-Type: application/x-www-form-urlencoded ) | |
c.FormFile("file") | 获取上传的文件(Content-Type: multipart/form-data ) | |
请求头操作 | c.GetHeader("User-Agent") | 获取请求头字段 |
c.Request.Header | 直接访问原始请求头(http.Header 类型) |
1.获取路由当中的静态值(Query):
r.GET("/test/:age", func(c *gin.Context) {age := c.Query("age")c.String(200, "I am %s years old", age)})
2.获取动态路由中的值(Param):
r.GET("/hello/:name", func(c *gin.Context) {name := c.Param("name")c.String(200, "hello %s", name)})
3.保存前端所上传的文件(如word,excel,png图片等):
r := gin.Default()// 设置静态文件目录,方便前端访问上传的文件r.Static("/uploads", "./uploads")// 处理文件上传r.POST("/upload", func(c *gin.Context) {// 获取名为 "file" 的上传文件file, err := c.FormFile("file")if err != nil {c.String(http.StatusBadRequest, fmt.Sprintf("get form file err: %s", err.Error()))return}// 获取上传文件的原始文件名filename := file.Filename// 指定文件保存路径destination := "./uploads/" + filename// 创建 uploads 目录(如果不存在)err = os.MkdirAll("./uploads", os.ModePerm)if err != nil {c.String(http.StatusInternalServerError, fmt.Sprintf("mkdir err: %s", err.Error()))return}// 保存上传的文件到指定位置if err := c.SaveUploadedFile(file, destination); err != nil {c.String(http.StatusInternalServerError, fmt.Sprintf("upload file err: %s", err.Error()))return}// 返回成功信息c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", filename))})
当然对于接受到的前端数据c,也可以进行后端变量的数据绑定:
方法 | 说明 |
---|---|
c.ShouldBindJSON(&obj) | 将请求体 JSON 绑定到结构体,自动触发 binding 标签验证 |
c.ShouldBindQuery(&obj) | 绑定 URL 查询参数到结构体 |
c.ShouldBindUri(&obj) | 绑定路由参数到结构体 |
c.Bind(&obj) | 自动根据 Content-Type 选择绑定方式,验证失败直接返回 400 错误 |
以下为一段示例代码:
type User struct {Name string `json:"name" binding:"required"`// 必填且必须是邮箱格式Email string `json:"email" binding:"required,email"`// 数值范围限制(18 ≤ Age ≤ 60)Age int `json:"age" binding:"gte=18,lte=60"`// 字符串长度限制(6 ≤ 密码长度 ≤ 16)Password string `json:"password" binding:"required,min=6,max=16"`// 枚举值(只能是 "male" 或 "female")Gender string `json:"gender" binding:"oneof=male female"`
}
func CreateUser(c *gin.Context) {var user Userif err := c.ShouldBindJSON(&user); err != nil {// 返回验证错误信息c.JSON(400, gin.H{"error": err.Error()})return}// 验证通过后的业务逻辑c.JSON(200, gin.H{"message": "User created"})
}
上述代码,会将前端传回的json数据c的值绑定在结构体变量user中,并验证数据是否符合结构体当中的binding要求,若不满足则会产生对应的错误err,否则err为空。
对于中间件与流程控制,有如下方法:
方法 | 说明 |
---|---|
c.Next() | 继续执行后续中间件或处理函数(通常在中间件中调用) |
c.Abort() | 终止当前请求的后续处理(如权限校验失败时) |
c.AbortWithStatus(code) | 终止并返回指定 HTTP 状态码 |
c.Set("key", value) | 存储数据供后续中间件或处理函数使用 |
c.Get("key") | 获取通过 Set 存储的值 |
中间件示例代码:
func AuthMiddleware() gin.HandlerFunc {return func(c *gin.Context) {token := c.GetHeader("Authorization")if token != "valid-token" {c.AbortWithStatusJSON(401, gin.H{"error": "Unauthorized"})return}c.Set("user", "admin") // 传递用户信息c.Next()}
}// 路由中使用中间件
router.GET("/admin", AuthMiddleware(), func(c *gin.Context) {user := c.MustGet("user").(string)c.String(200, "Welcome %s", user)
})
关于响应的请求头,以及Cookie,Session会话机制的内容,将在后续章节进一步详细解释。
6.Gin框架实战案例 -表单信息提交处理🌷
本次案例地前端是使用VUE3与Vite共同创建,采用的UI组件为ElementPlus-Form,如果你熟悉VUE2或其他UI组件库,当然也可以使用。这里就不过多叙述前端的搭建了,前端页面最终效果如下:
4.1 前端JS请求 🔅
对Create按键添加绑定函数,使得用户点击该按钮的时候,能够自动提交表单填写的信息。
接着使用Axios,将前端的表单数据发送到后端,当然熟悉fetch的uu,也可以使用fetch的方法进行数据传递。
4.2 后端Gin编写 🔅
首先先介绍一个概念-跨域请求(CORS):CORS(Cross-Origin Resource Sharing)是一种机制,允许服务器通过设置HTTP头部来指定哪些源(域、协议或端口)可以访问其资源。它用于解决浏览器的同源策略限制,使得不同源之间的资源可以安全地进行交互。
为了使得数据可以从前端传递到后端,首先我们得先配置一下Gin的CORS,具体代码如下:
r := gin.Default()// 自定义 CORS 中间件r.Use(func(c *gin.Context) {// 设置允许的请求头c.Writer.Header().Set("Access-Control-Allow-Origin", "*")c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")if c.Request.Method == "OPTIONS" {c.AbortWithStatus(http.StatusNoContent)return}c.Next()})
配置完CORS之后,开始编写处理函数部分:
r.POST("/submit-form", func(c *gin.Context) {var formData map[string]interface{}if err := c.BindJSON(&formData); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}c.JSON(http.StatusOK, gin.H{"message": "Form submitted successfully", "data": formData})})
这里与前端的请求相照应,由于前端发起的是POST方式,所以此处也使用r.POST,而不能使用r.GET,路径对应前端的请求路径,为 /submit-form 。
接着声明一个 map[string]interface{} 类型的变量formData,用于存储从请求体中解析出来的JSON数据。map[string]interface{}是一个通用的键值对结构,可以存储任意类型的值。(关于Go语言当中map数据类型的内容,我确实没写过...后续有时间在补上。)
c.BindJSON(&formData)
尝试从请求体中解析JSON数据,并将其绑定到formData
变量中。如果解析失败,err
会包含错误信息。如果解析失败,使用c.JSON
发送一个400状态码(http.StatusBadRequest
)和一个包含错误信息的JSON响应。如果JSON解析成功,使用c.JSON
发送一个200状态码(http.StatusOK
)和一个包含成功消息和解析数据的JSON响应。
最后依据前端的访问端口,进行对应的配置:
r.Run(":8000")
至此,后端部分完成,下面开始测试:
1.使用npm run开启前端界面,以及运行对应的后端Go文件。
2.在前端界面的表单中输入信息,并点击Create按钮进行表单数据的提交
3.在后端Gin文件中,可以查看到刚刚的前端请求信息
4.在前端界面中,使用F12,点击Console,可以在控制台看到,Gin所传回来的后端数据,接着前端接受,使用变量接收数据,便可以将数据渲染显示在网页界面上了。