文件上传
单文件上传
这里我准备了一张图片,我们想要实现当我们上传了一个图片的时候,这张图片可以出现在服务器中我们指定的位置
我们可以基于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框架中,我们可以使用Set
和Get
来实现对数据的设置与接收,代码如下:
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
这个子分组才可以使用这个中间件,我们就实现了一个很简单的路由分组中间件。