golang-gin包

文章目录

一、了解gin

Gin 是一个用 Golang编写的 高性能的web 框架, 由于http路由的优化,速度提高了近 40 倍。 Gin的特

点就是封装优雅、API友好。

特性类别 具体说明
性能表现 基于 Radix 树实现路由,匹配速度极快,性能接近原生 Go HTTP 服务器
API 设计 接口简洁直观,学习成本低,如 gin.Default() 快速创建实例,GET() / POST() 注册路由
中间件支持 内置日志、恢复 panic、静态文件服务等中间件,支持自定义中间件实现扩展功能
路由管理 支持路由分组(Group),便于按模块 / 版本 / 权限组织路由(如 /v1/*/admin/*
参数处理 提供便捷的请求参数绑定(ShouldBind),支持 JSON / 表单 / Query 等格式,可配合标签验证
数据响应 内置高效 JSON 处理,c.JSON() 快速返回 JSON 响应,性能优于标准库
错误处理 简化异常处理流程,c.AbortWithError() 等方法可快速返回结构化错误响应
模板渲染 集成 HTML 模板引擎,支持模板继承、自定义函数,适合传统 Web 应用开发
扩展性 设计灵活,易于与 ORM、缓存、认证等工具集成,生态丰富
轻量特性 核心代码简洁,依赖少,编译后二进制体积小,适合微服务或轻量应用

下载gin包

复制代码
go get -u github.com/gin-gonic/gin

测试一下

go 复制代码
package main
import "github.com/gin-gonic/gin"
func main() {
	router := gin.Default()
	router.GET("/", func(c *gin.Context) {
		c.JSON(200, map[string]interface{}{
			"name": "彪子",
			"age": 15,
		})
	})
	router.Run(":8080") // 不写默认,8080,监听并在 0.0.0.0:8080 上启动服务
}

①返回字符串

复制代码
package main
import "github.com/gin-gonic/gin"
func main() {
	router := gin.Default()
	// 类似printf的写法
	router.GET("/", func(c *gin.Context) {
		c.String(200, "我的值为:%v", "啊啊啊")
	})
	router.Run(":8080") // 不写默认,8080,监听并在 0.0.0.0:8080 上启动服务
}

②返回json

go 复制代码
package main
import "github.com/gin-gonic/gin"
func main() {
	router := gin.Default()
	router.GET("/", func(c *gin.Context) {
		c.JSON(200, map[string]interface{}{
			"name": "彪子",
			"age": 15,
		})
	})
	router.Run(":8080") // 不写默认,8080,监听并在 0.0.0.0:8080 上启动服务
}

或者可以将map类型用gin自带的,和我们写的一样

go 复制代码
router.GET("/", func(c *gin.Context) {
		c.JSON(200,gin.H{
			"name": "彪子",
			"age": 15,
		})
	})

也可以使用结构体

go 复制代码
package main
import (
	"github.com/gin-gonic/gin"
)
type Ariticle struct {
	Title string
	Content string
}
func main() {
	router := gin.Default()
	router.LoadHTMLGlob("templates/*") //配置模板文件
	router.GET("/", func(c *gin.Context) {
		article := &Ariticle{
			Title: "这是标题",
			Content: "这是内容",
		}
		c.JSON(200, article)
	})
	router.Run(":8080") // 不写默认,8080,监听并在 0.0.0.0:8080 上启动服务
}

二、html渲染

除了上诉的返回字符串和json数据,我们也可以渲染html。

如果模板目录下存在多个目录,每个目录下含有不同的html文件,那么我们这样操作

在html定义值

复制代码
{{ $title := .Title }}
<h1>{{ $title }}</h1>
{{ $sum := 10 + 20 }}
<p>总和:{{ $sum }}</p>

注释

复制代码
{{/* 这是模板注释,浏览器中看不到 */}}

条件判断

复制代码
{{ if .IsLogin }}
<p>欢迎回来,{{ .Username }}</p>
{{ else }}
<a href="/login">请登录</a>
{{ end }}
<!-- 否定判断 -->
{{ if not .IsEmpty }}
<p>内容不为空</p>
{{ end }}

循环遍历

复制代码
<!-- 遍历切片/数组 -->
<ul>
{{ range .Items }}
	<li>{{ . }}</li> <!-- 内部的 . 代表当前元素 -->
{{ end }}
</ul>

<!-- 带索引的遍历 -->
<!-- 使用 {{ . }} 时注意上下文变化(如在 range 内部,. 代表当前元素) -->
{{ range $index, $item := .Items }}
	<p>{{ $index }}: {{ $item.Name }}</p>
{{ end }}

<!-- 遍历映射 -->
{{ range $key, $value := .UserInfo }}
	<p>{{ $key }}: {{ $value }}</p>
{{ end }}

<!-- 空集合处理 -->
{{ range .Items }}
	<li>{{ . }}</li>
{{ else }}
	<p>没有数据</p> <!-- 当集合为空时执行 -->
{{ end }}

原始html输出

复制代码
<!-- 假设 .Content 包含 <strong>文本</strong> -->
{{ .Content }} <!-- 输出:&lt;strong&gt;文本&lt;/strong&gt; -->
{{ .Content | safeHTML }} <!-- 输出:<strong>文本</strong> -->

三、gin中get/post获取值

get请求

复制代码
name:=c.Query("name")
age:=c.DefaultQuery("age","18")

post请求

复制代码
name := c.PostForm("name")
age := c.DefaultPostForm("age", "18") // 默认值为string类型

将get/post请求的值绑定都按结构体

go 复制代码
package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)
// 定义接收参数的结构体
type UserQuery struct {
	// form标签指定对应的查询参数名
	Name string `form:"name"`
	Page int `form:"page" binding:"default=1"` // 默认值1
}
func main() {
	r := gin.Default()
	// 注册路由
	r.GET("/user", func(c *gin.Context) {
		var query UserQuery
		// 绑定GET请求参数到结构体
		if err := c.ShouldBindQuery(&query); err == nil {
			// 绑定成功,使用参数
			c.JSON(http.StatusOK, query)
			return
		}
	})
	// 启动服务
	r.Run(":8080")
}

动态路由传值

复制代码
package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)
func main() {
	r := gin.Default()
	// 注册路由
	r.GET("/:uid", func(c *gin.Context) {
		uid:=c.Param("uid")
		// 绑定GET请求参数到结构体
		c.String(http.StatusOK, "uid: %s", uid)
	})
	// 启动服务
	r.Run(":8080")
}

四、路由分组

go 复制代码
package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)
func main() {
	r := gin.Default()
	// 1. 基础路由分组 - 添加路径前缀
	userGroup := r.Group("/user")
	{
		// 实际路径: /user
		userGroup.GET("", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{"message": "用户列表"})
		})
		// 实际路径: /user/:id
		userGroup.GET("/:id", func(c *gin.Context) {
			id := c.Param("id")
			c.JSON(http.StatusOK, gin.H{"message": "查看用户", "id": id})
		})
		// 实际路径: /user/create
		userGroup.POST("/create", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{"message": "创建用户"})
		})
	}
	// 2. 带中间件的路由分组
	// 创建需要身份验证的路由分组
	authGroup := r.Group("/admin")
	// 为该分组添加中间件(如身份验证)
	authGroup.Use(AuthMiddleware())
	{
		// 实际路径: /admin/dashboard,访问前会经过AuthMiddleware验证
		authGroup.GET("/dashboard", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{"message": "管理员面板"})
		})
		// 实际路径: /admin/settings
		authGroup.GET("/settings", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{"message": "系统设置"})
		})
	}
	// 3. 嵌套路由分组
	v1 := r.Group("/api/v1")
	{
		// 嵌套用户分组,实际路径: /api/v1/users
		users := v1.Group("/users")
		{
			users.GET("", func(c *gin.Context) {
				c.JSON(http.StatusOK, gin.H{"message": "API v1 用户列表"})
			})
		}
		// 嵌套文章分组,实际路径: /api/v1/articles
		articles := v1.Group("/articles")
		{
			articles.GET("", func(c *gin.Context) {
				c.JSON(http.StatusOK, gin.H{"message": "API v1 文章列表"})
			})
		}
	}
	r.Run(":8080")
}
// 定义一个身份验证中间件示例
func AuthMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 实际应用中这里会验证token或session
		token := c.GetHeader("Authorization")
		if token == "" {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "未授权访问"})
			c.Abort() // 终止请求处理链
			return
		}
		// 验证通过,继续处理请求
		c.Next()
	}
}

如果把不同路由放到不同文件

五、中间件

c.Next() 的核心作用

流程控制:在中间件中调用 c.Next() 会暂停当前中间件的执行,先执行后续的中间件或路由处理器。

前后置操作: c.Next() 调用前的代码可以视为 "前置操作"(如权限验证、日志记录开始),调用后的代码可以视为 "后置操作"(如日志记录结束、响应处理)。

全局中间件:可以使后续的所有请求都走此中间件

复制代码
func main() {
	r := gin.Default()
	r.Use(mid1, mid2)
	// 实际路径: /user
	r.GET("/", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"message": "成功"})
	})
	r.Run(":8080")
}

在分组路由后配置中间件的两种方式

复制代码
// 方式1
a:=r.Group("admin",mid1, mid2)
// 方式2
a:=r.Group("admin")
a.Use(mid1, mid2)

在不同中间件传递值

gin中间件中使用grountine

六、文件上传

单个文件上传

go 复制代码
package main

import (
	"net/http"
	"os"
	"path/filepath"

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

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

	// 单个文件上传
	r.POST("/upload", func(c *gin.Context) {
		// 获取上传的文件
		file, err := c.FormFile("file")
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"error": "获取文件失败: " + err.Error(),
			})
			return
		}

		// 定义保存路径(当前目录下的uploads文件夹)
		savePath := "./uploads"
		// 确保保存目录存在
		if err := ensureDir(savePath); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{
				"error": "创建保存目录失败: " + err.Error(),
			})
			return
		}

		// 构建完整的保存路径(目录+文件名)
		dst := filepath.Join(savePath, file.Filename)

		// 使用SaveUploadedFile保存文件
		if err := c.SaveUploadedFile(file, dst); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{
				"error": "保存文件失败: " + err.Error(),
			})
			return
		}

		c.JSON(http.StatusOK, gin.H{
			"message":  "文件上传成功",
			"filename": file.Filename,
			"savePath": dst,
		})
	})

	r.Run(":8080")
}

// 确保目录存在,不存在则创建
func ensureDir(dir string) error {
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		return os.MkdirAll(dir, 0755)
	}
	return nil
}

多文件上传

go 复制代码
package main

import (
	"net/http"
	"os"
	"path/filepath"

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

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

	// 多个文件上传
	r.POST("/upload-multiple", func(c *gin.Context) {
		// 解析多部分表单
		form, err := c.MultipartForm()
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"error": "解析表单失败: " + err.Error(),
			})
			return
		}

		// 获取所有名为"file"的文件
		files := form.File["file"]
		if len(files) == 0 {
			c.JSON(http.StatusBadRequest, gin.H{
				"error": "未找到上传的文件",
			})
			return
		}

		// 定义保存路径
		savePath := "./uploads"
		if err := ensureDir(savePath); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{
				"error": "创建保存目录失败: " + err.Error(),
			})
			return
		}

		// 保存所有文件
		uploadedFiles := make([]string, 0, len(files))
		for _, file := range files {
			dst := filepath.Join(savePath, file.Filename)
			if err := c.SaveUploadedFile(file, dst); err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{
					"error": "保存文件 " + file.Filename + " 失败: " + err.Error(),
				})
				return
			}
			uploadedFiles = append(uploadedFiles, file.Filename)
		}

		c.JSON(http.StatusOK, gin.H{
			"message":   "所有文件上传成功",
			"fileCount": len(uploadedFiles),
			"filenames": uploadedFiles,
		})
	})

	r.Run(":8080")
}

// 确保目录存在,不存在则创建
func ensureDir(dir string) error {
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		return os.MkdirAll(dir, 0755)
	}
	return nil
}

七、gin中的cookie

go 复制代码
package main

import (
	"net/http"
	"strings"

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

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

	// 设置Cookie
	r.GET("/set_cookie", func(c *gin.Context) {
		// 设置一个名为"username",值为"JohnDoe",1小时后过期的Cookie
		c.SetCookie("username", "JohnDoe", 3600, "/", "example.com", false, true)
		c.JSON(http.StatusOK, gin.H{"message": "Cookie set successfully"})
	})

	// 获取 Cookie
	r.GET("/get_cookie", func(c *gin.Context) {
		cookie, err := c.Cookie("username")
		if err != nil {
			c.JSON(http.StatusOK, gin.H{"message": "Cookie not found", "error": err.Error()})
			return
		}
		c.JSON(http.StatusOK, gin.H{"message": "Cookie retrieved successfully", "username": cookie})
	})

	//删除 Cookie
	r.GET("/delete_cookie", func(c *gin.Context) {
		// 将过期时间设置为1小时前,以达到删除Cookie的效果
		c.SetCookie("username", "", -3600, "/", "example.com", false, true)
		c.JSON(http.StatusOK, gin.H{"message": "Cookie deleted successfully"})
	})
	
	// 遍历Cookie
	r.GET("/list_cookies", func(c *gin.Context) {
		cookies := c.Request.Cookies()
		var cookieList []string
		for _, cookie := range cookies {
			cookieList = append(cookieList, cookie.Name+"="+cookie.Value)
		}
		c.JSON(http.StatusOK, gin.H{"message": "List of cookies", "cookies": strings.Join(cookieList, ", ")})
	})

	r.Run(":8080")
}
相关推荐
Scott9999HH3 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社4 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海4 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖5 小时前
JDK 26 新特性详解
java·开发语言
马优晨5 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区7 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大8 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai8 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜8 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa8 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio