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")
}
相关推荐
小莞尔5 小时前
【51单片机】【protues仿真】基于51单片机停车场的车位管理系统
c语言·开发语言·单片机·嵌入式硬件·51单片机
Sally璐璐6 小时前
Go正则表达式实战指南
数据库·mysql·golang
yuluo_YX6 小时前
Go Style 代码风格规范
开发语言·后端·golang
百锦再6 小时前
脚本语言的大浪淘沙或百花争艳
java·开发语言·人工智能·python·django·virtualenv·pygame
上位机付工7 小时前
C#与倍福TwinCAT3进行ADS通信
开发语言·c#
励志不掉头发的内向程序员7 小时前
STL库——二叉搜索树
开发语言·c++·学习
至此流年莫相忘7 小时前
设计模式:模板方法模式
java·开发语言·设计模式
土了个豆子的8 小时前
02.继承MonoBehaviour的单例模式基类
开发语言·visualstudio·单例模式·c#·里氏替换原则
qq_172805598 小时前
Go 自建库的使用教程与测试
开发语言·后端·golang