Dawn's Blogs

分享技术 记录成长

0%

Gin基本使用 (3) 文件上传、重定向和路由

文件上传

单文件

使用FormFile方法可以获取到POST请求中上传的文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func main() {
r := gin.Default()
r.LoadHTMLFiles("upload.html")

r.GET("/upload", func(c *gin.Context) {
c.HTML(http.StatusOK, "upload.html", nil)
})

r.POST("/upload", func(c *gin.Context) {
// 从POST请求中读取文件
f, err := c.FormFile("f1")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
} else {
// 把文件保存到本地
dst := fmt.Sprintf("./%s", f.Filename)
c.SaveUploadedFile(f, dst)
c.JSON(http.StatusOK, gin.H{
"status": "OK",
})
}
})

r.Run(":9090")
}

多文件

请求头参数设置 Content-Type: multipart/form-data,多文件上传请求为:

1
2
3
4
curl -X POST http://localhost:8080/upload \
-F "upload[]=@/Users/appleboy/test1.zip" \
-F "upload[]=@/Users/appleboy/test2.zip" \
-H "Content-Type: multipart/form-data"

首先解析 multipart forms,然后获取文件列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func main() {
router := gin.Default()
// 为 multipart forms 设置较低的内存限制 (默认是 32 MiB)
router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// Multipart form
form, _ := c.MultipartForm()
files := form.File["upload[]"]

for _, file := range files {
log.Println(file.Filename)

// 上传文件至指定目录
c.SaveUploadedFile(file, dst)
}
c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))
})
router.Run(":8080")
}

重定向

HTTP重定向

使用Redirect方法即可进行HTTP重定向:

1
2
3
r.GET("/index", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "http://www.baidu.com")
})

通过 POST 方法进行 HTTP 重定向(这里可以使用 StatusMovedPermanently 301 状态码、StatusFound 302 状态码和 StatusSeeOther 303 状态码)。

1
2
3
r.POST("/test", func(c *gin.Context) {
c.Redirect(http.StatusFound, "/foo")
})

303 状态码明确表示客户端应当采用 GET 方法获取资源,这点与 302 状态码有区别。

当 301、302、303 响应状态码返回时,几乎所有的浏览器都会把 POST 改成 GET,并删除请求报文内的主体,之后请求会自动再次发送。301、302 标准是禁止将 POST 方法改变成 GET 方法的,但实际使用时大家都会这么做。

路由重定向

可以使用HandleContext函数继续处理,进行路由重定向:

1
2
3
4
5
6
7
8
9
10
11
r.GET("/index1", func(c *gin.Context) {
// 跳转到/index2
c.Request.URL.Path = "/index2"
r.HandleContext(c)
})

r.GET("/index2", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"status": "OK",
})
})

路由

普通路由

除了GETPOSTPUTDELETE等HTTP请求方法外,还有一个可以匹配所有请求的Any方法:

1
2
3
4
5
r.Any("/index", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"status": "OK",
})
})

为没有配置处理函数的路由添加处理程序,默认情况下它返回404,下面的代码为没有匹配到的路由都返回404.html页面:

1
2
3
r.NoRoute(func(c *gin.Context) {
c.HTML(http.StatusNotFound, "404.html", nil)
})

路由组

将拥有共同URL前缀的路由划分为一个路由组。可以用Group方法进行路由分组,习惯性一对{}包裹同组的路由:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func main() {
r := gin.Default()

// 用户页面路由组
userGroup := r.Group("/user")
{
userGroup.GET("/index", func(c *gin.Context) { /* ... */ })
userGroup.GET("/login", func(c *gin.Context) { /* ... */ })
}

// 视频页面路由组
videoGroup := r.Group("/video")
{
videoGroup.GET("/index", func(c *gin.Context) { /* ... */ })
videoGroup.GET("/movie", func(c *gin.Context) { /* ... */ })
videoGroup.GET("/cartoon", func(c *gin.Context) { /* ... */ })
}

r.Run(":9090")
}

路由组支持嵌套

1
2
3
4
5
6
7
8
9
10
11
// 商城页面路由组
shopGroup := r.Group("/shop")
{
shopGroup.GET("/index", func(c *gin.Context) { /* ... */ })
// 嵌套路由组
phoneGroup := shopGroup.Group("/phone")
{
phoneGroup.GET("/iphone", func(c *gin.Context) { /* ... */ })
phoneGroup.GET("/xiaomi", func(c *gin.Context) { /* ... */ })
}
}