GO语言学习(16)Gin后端框架

目录

☀️前言

1.什么是前端?什么是后端?🌀

[2.Gin框架介绍 🌷](#2.Gin框架介绍 🌷)

[3.Gin框架的基本使用 -Hello,World例子🌷](#3.Gin框架的基本使用 -Hello,World例子🌷)

[🌿入门示例 - Hello,World](#🌿入门示例 - Hello,World)

💻补充(一些常用的网络术语):

[3.1 Get请求与Post请求📞](#3.1 Get请求与Post请求📞)

[3.2 状态码 🔧](#3.2 状态码 🔧)

[4.Gin的各类返回方式 🌷](#4.Gin的各类返回方式 🌷)

✏️返回字符串:

✏️返回xml:

✏️返回文件(如Excel,txt,png等文件):

✏️页面重定向:

5.如何处理前端的请求数据🌷

[6.Gin框架实战案例 -表单信息提交处理🌷](#6.Gin框架实战案例 -表单信息提交处理🌷)

[4.1 前端JS请求 🔅](#4.1 前端JS请求 🔅)

[4.2 后端Gin编写 🔅](#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,同时保持高性能和低资源占用。其主要特点如下:

  1. 高性能:基于Radix树的路由实现,内存占用小,无反射,API性能可预测。
  2. 零分配路由器:路由处理过程中不产生内存分配,确保高效。
  3. 中间件支持:支持链式中间件,可以用于日志记录、认证、GZIP压缩等。
  4. 崩溃恢复:能够捕获HTTP请求中的panic并恢复,确保服务器始终可用。
  5. JSON验证:支持对请求中的JSON数据进行验证,确保数据完整性。
  6. 路由分组:支持路由分组,便于组织不同功能的API版本或权限。
  7. 错误管理:提供方便的方式收集和处理HTTP请求中的错误。
  8. 内置渲染:支持JSON、XML和HTML的渲染。
  9. 可扩展性:易于创建自定义中间件,满足特定需求

同样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框架进行开发了。

bash 复制代码
$ go get -u github.com/gin-gonic/gin
🌿入门示例 - Hello,World
Go 复制代码
package main

import "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请求处理函数。当客户端访问应用的根路径"/"时,会调用这个处理函数。cgin.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格式,下面介绍一些别的格式的数据返回。

✏️返回字符串:

Go 复制代码
router.GET("/str", func(c *gin.Context) {
  c.String(http.StatusOK, "返回成功")
})

✏️返回xml:

Go 复制代码
router.GET("/xml", func(c *gin.Context) {
  c.XML(http.StatusOK, gin.H{"user": "FJNU", "message": "hey", "status": http.StatusOK})
})

✏️返回文件(如Excel,txt,png等文件):

Go 复制代码
 router.GET("/download/:filename", func(c *gin.Context) {
        filename := c.Param("filename")           // 获取前端传输的文件名
        filePath := "./files/" + filename        // 获取本地的文件路径
        c.File(filePath)              // 将对应的文件传回前端
   })

上述方法用于返回单个文件,filename必须包含文件的扩展名(如.png),Gin会自动根据文件扩展名设置正确的MIME类型。

如果希望文件被浏览器作为附件下载 而不是直接显示,可以使用**c.FileAttachment()**方法。

Go 复制代码
package main

import "github.com/gin-gonic/gin"

func main() {
    router := gin.Default()

    router.GET("/download/:filename", func(c *gin.Context) {
        filename := c.Param("filename")
        filePath := "./files/" + filename
        c.FileAttachment(filePath, filename)
    })

    router.Run(":8080")
}

✏️页面重定向:

如果你希望点击某个按钮后,跳转到另一个页面,可以使用以下方法:

Go 复制代码
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/:idid
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):

Go 复制代码
r.GET("/test/:age", func(c *gin.Context) {
		age := c.Query("age")
		c.String(200, "I am %s years old", age)
	})

2.获取动态路由中的值(Param):

Go 复制代码
r.GET("/hello/:name", func(c *gin.Context) {
		name := c.Param("name")
		c.String(200, "hello %s", name)
	})

3.保存前端所上传的文件(如word,excel,png图片等):

Go 复制代码
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 错误

以下为一段示例代码:

Go 复制代码
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 User
    if 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 存储的值

中间件示例代码:

Go 复制代码
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,具体代码如下:

Go 复制代码
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之后,开始编写处理函数部分:

Go 复制代码
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响应。

最后依据前端的访问端口,进行对应的配置:

Go 复制代码
r.Run(":8000")

至此,后端部分完成,下面开始测试:

1.使用npm run开启前端界面,以及运行对应的后端Go文件。

2.在前端界面的表单中输入信息,并点击Create按钮进行表单数据的提交

3.在后端Gin文件中,可以查看到刚刚的前端请求信息

4.在前端界面中,使用F12,点击Console,可以在控制台看到,Gin所传回来的后端数据,接着前端接受,使用变量接收数据,便可以将数据渲染显示在网页界面上了。

相关推荐
吴梓穆2 分钟前
UE5学习笔记 FPS游戏制作38 继承标准UI
笔记·学习·ue5
Three~stone26 分钟前
MySQL学习集--DDL
数据库·sql·学习
胡斌附体27 分钟前
qt socket编程正确重启tcpServer的姿势
开发语言·c++·qt·socket编程
齐尹秦37 分钟前
HTML 音频(Audio)学习笔记
学习
白露与泡影1 小时前
Java面试题及答案整理( 2025年 4 月最新版,持续更新)
java·开发语言
V---scwantop---信1 小时前
英文字体:大胆都市街头Y2Y涂鸦风格品牌海报专辑封面服装字体 Chrome TM – Graffiti Font
笔记·字体
瞌睡不来1 小时前
(学习总结32)Linux 基础 IO
linux·学习·io
YueiL1 小时前
C++入门练习之 给出年分m和一年中的第n天,算出第n天是几月几号
开发语言·c++·算法
Moonnnn.1 小时前
运算放大器(四)滤波电路(滤波器)
笔记·学习·硬件工程
冷凝女子1 小时前
【QT】获取文件路径中的文件名,去掉后缀,然后提取文件名中的数字
开发语言·数据库·qt