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

简介: 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这个子分组才可以使用这个中间件,我们就实现了一个很简单的路由分组中间件。

相关文章
|
2月前
|
数据采集 中间件 开发者
Scrapy爬虫框架-自定义中间件
Scrapy爬虫框架-自定义中间件
60 1
|
1月前
|
消息中间件 NoSQL Java
springboot整合常用中间件框架案例
该项目是Spring Boot集成整合案例,涵盖多种中间件的使用示例,每个案例项目使用最小依赖,便于直接应用到自己的项目中。包括MyBatis、Redis、MongoDB、MQ、ES等的整合示例。
109 1
|
3月前
|
缓存 中间件 网络架构
Python Web开发实战:高效利用路由与中间件提升应用性能
在Python Web开发中,路由和中间件是构建高效、可扩展应用的核心组件。路由通过装饰器如`@app.route()`将HTTP请求映射到处理函数;中间件则在请求处理流程中插入自定义逻辑,如日志记录和验证。合理设计路由和中间件能显著提升应用性能和可维护性。本文以Flask为例,详细介绍如何优化路由、避免冲突、使用蓝图管理大型应用,并通过中间件实现缓存、请求验证及异常处理等功能,帮助你构建快速且健壮的Web应用。
35 1
|
5月前
|
JSON 中间件 数据处理
实践出真知:通过项目学习Python Web框架的路由与中间件设计
【7月更文挑战第19天】探索Python Web开发,掌握Flask或Django的关键在于理解路由和中间件。路由连接URL与功能,如Flask中@app.route()定义请求响应路径。中间件在请求处理前后执行,提供扩展功能,如日志、认证。通过实践项目,不仅学习理论,还能提升构建高效Web应用的能力。示例代码展示路由定义及模拟中间件行为,强调动手实践的重要性。
60 1
|
5月前
|
缓存 监控 安全
中间件在Python Web框架中的角色与应用场景
【7月更文挑战第21天】中间件在Python Web开发中作为服务器与应用间的软件层,拦截、处理请求和响应,无需改动应用代码。它扩展框架功能,复用跨应用逻辑,加强安全,优化性能。如Django中间件处理请求/响应,Flask通过WSGI中间件实现类似功能,两者均在不触及核心代码前提下,灵活增强应用行为,是现代Web开发关键组件。
62 0
|
5月前
|
缓存 中间件 网络架构
Python Web开发实战:高效利用路由与中间件提升应用性能
【7月更文挑战第20天】在Python Web开发中,路由与中间件是构建高效应用的核心。路由通过装饰器如`@app.route()`在Flask中映射请求至处理函数;中间件(如`@app.before_request`, `@app.after_request`)则在请求流程中插入自定义逻辑。优化路由包括减少冲突、利用动态参数及蓝图;中间件可用于缓存响应、请求验证和异常处理,显著提升性能和可维护性。良好设计是关键,示例代码展示了如何在Flask中实现这些策略。
63 0
|
5月前
|
中间件 数据库 开发者
解析Python Web框架的四大支柱:模板、ORM、中间件与路由
【7月更文挑战第20天】Python Web框架如Django、Flask、FastAPI的核心包括模板(如Django的DTL和Flask的Jinja2)、ORM(Django的内置ORM与Flask的SQLAlchemy)、中间件(Django的全局中间件与Flask的装饰器实现)和路由(Django的urls.py配置与Flask的@app.route()装饰器)。这些组件提升了代码组织和数据库操作的便捷性,确保了Web应用的稳定性和可扩展性。
70 0
|
5月前
|
中间件 API 开发者
深入理解Python Web框架:中间件的工作原理与应用策略
【7月更文挑战第19天】Python Web中间件摘要:**中间件是扩展框架功能的关键组件,它拦截并处理请求与响应。在Flask中,通过`before_request`和`after_request`装饰器模拟中间件行为;Django则有官方中间件系统,需实现如`process_request`和`process_response`等方法。中间件用于日志、验证等场景,但应考虑性能、执行顺序、错误处理和代码可维护性。
89 0
|
6月前
|
消息中间件 存储 中间件
【消息中间件】详解三大MQ:RabbitMQ、RocketMQ、Kafka
【消息中间件】详解三大MQ:RabbitMQ、RocketMQ、Kafka
1653 0
|
5月前
|
消息中间件 编解码 Docker
Docker部署RabbitMQ消息中间件
【7月更文挑战第4天】Docker部署RabbitMQ消息中间件
281 3