Gin框架学习笔记(五) ——文件上传与路由中间件

文件上传

单文件上传

这里我准备了一张图片,我们想要实现当我们上传了一个图片的时候,这张图片可以出现在服务器中我们指定的位置,图片如下:
在这里插入图片描述
我们可以基于Gin框架来实现我们上述的操作

package main

import "C"
import (
	"fmt"
	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	r.MaxMultipartMemory = 8 << 20 // 8 MiB,
	r.POST("/upload", func(c *gin.Context) {
		file, _ := c.FormFile("file")
		fmt.Println(file.Filename)
		dst := "./Source/" + file.Filename
		c.SaveUploadedFile(file, dst)
	})
	r.Run(":8080")
}

编译运行:
在这里插入图片描述
在这里插入图片描述

保存上传文件

  • SaveUploadedFile函数
    函数签名:
func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string)

file:要保存的文件
dst:保存的路径

  • Create and Copy
    这个方法其实也好理解,我们读取上传文件的内容,并且以这份文件的名字创建一个文件,将读到的内容储存到这个文件中,代码如下:
package main

import "C"
import (
	"fmt"
	"github.com/gin-gonic/gin"
	"io"
	"os"
)

func main() {
	r := gin.Default()
	r.MaxMultipartMemory = 8 << 20 // 8 MiB,
	r.POST("/upload", func(c *gin.Context) {
		file, _ := c.FormFile("file")
		fmt.Println(file.Filename)
		fileRead, _ := os.Open(file.Filename)
		defer fileRead.Close()
		dst := "./Source/" + file.Filename
		fileWrite, _ := os.Create(dst)
		defer fileWrite.Close()
		io.Copy(fileWrite, fileRead)
	})
	r.Run(":8080")
}

多文件上传

func file2(c *gin.Context) { //处理多文件上传
	form, _ := c.MultipartForm() //获取表单数据
	files := form.File["upload[]"]
	for _, file := range files {
		fmt.Println(file.Filename)
		dst := "./Source/" + file.Filename
		c.SaveUploadedFile(file, dst)
	}
	c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))
}

路由中间件

前言

我们在日常开发的时候可能忽悠很多公共的业务逻辑,比如说:登录验证,权限校验,数据分页,记录日志等等,我们在每一个模块下都进行相关逻辑的书写无疑是不现实的,在Gin框架中它允许我们在
处理请求的同时自主加入自己的钩子(hook)函数来处理这些公共的业务逻辑,而这些钩子函数也就是我们所说的中间件。

单独注册路由中间件

示例

下面我们首先来看一个很简单的例子:

package main

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

type UserInfo struct {
	Username string `json:"username"`
	Age      int    `json:"age"`
}

func main() {
	r := gin.Default()
	r.GET("/", func(c *gin.Context) {
		user := UserInfo{
			Username: "张三",
			Age:      18,
		}
		c.JSON(200, user)
	})
	r.Run(":8080")
}

运行并向其发出请求:
在这里插入图片描述
如上所示,这里的闭包函数就是一个典型的Hook函数,也就是我们所说的中间件。
注意:中间件的类型必须是gin.HandlerFunc类型

单独注册中间件的细节

  • 多个中间件的使用
    我们来看一下GET这一类函数的函数签名:
	func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes {
	return group.handle(http.MethodPost, relativePath, handlers)
}

// GET is a shortcut for router.Handle("GET", path, handlers).
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
	return group.handle(http.MethodGet, relativePath, handlers)
}

// DELETE is a shortcut for router.Handle("DELETE", path, handlers).
func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes {
	return group.handle(http.MethodDelete, relativePath, handlers)
}

// PATCH is a shortcut for router.Handle("PATCH", path, handlers).
func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) IRoutes {
	return group.handle(http.MethodPatch, relativePath, handlers)
}

我们可以发现这里第二个参数的类型为:...HandlerFunc,这意味着我们可以使用在一个请求处理中使用多个中间件函数,比如下面这样:

package main

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

type UserInfo struct {
	Username string `json:"username"`
	Age      int    `json:"age"`
}

func m1(c *gin.Context) {
	user := UserInfo{
		Username: "张三",
		Age:      18,
	}
	c.JSON(200, user)
}

func m2(c *gin.Context) {
	user := UserInfo{
		Username: "李四",
		Age:      20,
	}
	c.JSON(200, user)
}

func main() {
	r := gin.Default()
	r.GET("/", m1, m2)
	r.Run(":8080")
}

运行结果如下:
在这里插入图片描述

  • 请求中间件与响应中间件
    一个中间件实现的功能主要是有两部分:对接收到的请求进行相关处理处理即将发送回客户端的响应,而我们根据实现的功能不同其实也可以将一个中间件分为两部分:请求中间件响应中间件,而我们也有专门函数去控制它们的工作,比如下面这个例子:
package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
)

func m1(c *gin.Context) {
	fmt.Println("m1 in")
	c.Next()
	fmt.Println("m1 out")
}

func m2(c *gin.Context) {
	fmt.Println("m2 in")
	c.Next()
	fmt.Println("m2 out")
}

func m3(c *gin.Context) {
	fmt.Println("m3 in")
	c.Next()
	fmt.Println("m3 out")
}

func main() {
	r := gin.Default()
	r.GET("/user", m1, m2, m3)
	r.Run(":8080")
}

运行上面的代码,它的结果是这样的:

m1 in
m2 in
m3 in
m3 out
m2 out
m1 out

我们可以知道它的过程他该是这样的:
在这里插入图片描述
这就是Next函数的作用了,它先完成请求中间件的工作部分,再完成响应中间件的工作部分,有趣的是它的模型有点类似于栈的先进后出,大家记忆的时候也可以仿照这个来记忆。

除此之外,我们还可以用Abort函数来拦截响应,示例如下:

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
)

func m1(c *gin.Context) {
	fmt.Println("m1 in")
	c.Abort()
	fmt.Println("m1 out")
}

func m2(c *gin.Context) {
	fmt.Println("m2 in")
	c.Next()
	fmt.Println("m2 out")
}

func m3(c *gin.Context) {
	fmt.Println("m3 in")
	c.Next()
	fmt.Println("m3 out")
}

func main() {
	r := gin.Default()
	r.GET("/user", m1, m2, m3)
	r.Run(":8080")
}

运行结果为:

m1 in
m1 out

我们可以看到Abort()拦截,后续的HandlerFunc就没有执行了

全局注册中间件

package main

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

func m10(c *gin.Context) {
	c.JSON(200, gin.H{
		"message": "m1",
	})
}

func main() {
	r := gin.Default()
	r.Use(m10) //注册全局中间件
	r.GET("/", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "hello",
		})
	})
	r.Run(":8080")
}

运行结果为:
在这里插入图片描述
我们可以发现,虽然我们没有调用m1,但是还是出现了m1的Json响应数据,而这也就是全局注册中间件的作用。

基于中间件传递数据

在Gin框架中,我们可以使用SetGet来实现对数据的设置与接收,代码如下:

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
)

func m11(c *gin.Context) {
	fmt.Println("m11")
	c.Set("name", "fengxu")
}

func main() {
	r := gin.Default()
	r.Use(m10,m11)
	r.GET("/", func(c *gin.Context) {
		name, _ := c.Get("name")
		c.JSON(200, gin.H{
			"name": name,
		})
	})
	r.Run(":8080")
}

运行结果如下:
在这里插入图片描述
细节
这里Get函数的签名如下:

func (c *Context) Set(key string, value any)

所以我们传入任意类型的值,这里我们演示如何传入结构体并获取结构体内指定字段的值:

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
)

type UserInfo struct {
	Username string `json:"username"`
	Age      int    `json:"age"`
	Sex      string `json:"sex"`
}
func m12(c *gin.Context) {
	user := UserInfo{
		Username: "luoyu",
		Age:      18,
		Sex:      "男",
	}
	c.Set("user", user)
}

func main() {
	r := gin.Default()
	r.Use(m12)
	r.GET("/", m11, m12, func(c *gin.Context) {
		_user, _ := c.Get("user")
		user := _user.(UserInfo)
		c.JSON(200, gin.H{
			"name": user.Username,
		})
	})
	r.Run(":8080")
}

运行结果:
在这里插入图片描述

路由分组

基于路由分组的路由管理

在我们日常开发时,可能我们会有很多个路由中间件,如何有效的管理它们就成了一个很大的问题,而这里我们就介绍的是如何基于路由分组来有效的管理路由,首先我们来看一下示例代码:

package main

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

func main() {
	r := gin.Default()
	api := r.Group("api") //设置总的路由

	api.GET("/user", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"msg": "user",
		})
	})

	api.GET("/admin", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"msg": "admin",
		})
	})

	r.Run(":8080")
}

在这里插入图片描述
在这里插入图片描述
我们可以利用这种分组的方式来有效的实现对路由的组织,防止由于路由过多造成不必要的混乱。

路由分组中间件

路由分组中间件的注册

在Gin框架中,可以去为路由分组去专门订制中间件,以下面的代码为例:

package main

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

type Res struct {
	Code int    `json:"code"`
	Data any    `json:"data"`
	Msg  string `json:"msg"`
}

func m11(c *gin.Context) {
	c.Next()
	c.JSON(200, Res{
		Code: 200,
		Data: "abc",
		Msg:  "响应成功",
	})
}

func main() {
	r := gin.Default()
	api := r.Group("api")
	login := api.Group("login").Use(m11)
	login.GET("/", func(c *gin.Context) {
		c.JSON(200, "ok")
	})
	r.Run(":8080")
}

运行并测试:
在这里插入图片描述
在这里插入图片描述
我们可以看到这个中间件只有login这个子分组才可以使用这个中间件,我们就实现了一个很简单的路由分组中间件。

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落雨便归尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值