Gin 是 Go语言写的一个 web 框架,它具有运行速度快,分组的路由器,良好的崩溃捕获和错误处理,非常好的支持中间件和 json。
框架介绍
环境配置
下载Gin包
首先下载安装 gin 包:
go get -u github.com/gin-gonic/gin
需要注意的是Gin框架需要Golang 1.13以上版本
有的时候会碰到因为GitHub被墙导致无法获取的情况,
这时候需要改一下Go module 的模块代理,改成国内代理的网站,可以点击下面网址来更改模块代理
七牛云 - Goproxy.cn
测试搭建
创建main.go,输入如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "搭建完成")
})
r.Run(":8888") // 端口号8888
}
|
运行main.go

然后访问http://127.0.0.1:8888/

显示在“搭建完成”,则说明Gin环境完成了搭建
路由 (Route)
Gin
的路由支持 GET
, POST
, PUT
, DELETE
, PATCH
, HEAD
, OPTIONS
请求,同时还有一个 Any
函数,可以同时支持以上的所有请求。
当然也可以使用 context.Any()
这个方法来匹配所有请求.
无参数路由
1
2
3
|
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Hello,World!")
})
|
可以使用 curl + 请求地址来快捷访问服务器
1
|
curl http://localhost.8888/
|
返回结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
StatusCode : 200
StatusDescription : OK
Content : Hello,World!
RawContent : HTTP/1.1 200 OK
Content-Length: 12
Content-Type: text/plain; charset=utf-8
Date: Sun, 20 Nov 2022 10:04:09 GMT
Hello,World!
Forms : {}
Headers : {[Content-Length, 12], [Content-Type, text/plain; charset=utf-8], [Date, Sun, 20 Nov 2022 10:04:09
GMT]}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : mshtml.HTMLDocumentClass
RawContentLength : 12
|
完整代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package main
import (
"fmt"
"github.com/gin-gonic/gin" "net/http")
func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Hello,World!")
})
r.Run(":8888") // 端口号8888
}
|
解析路径参数
有的时候我们需要动态的路由,例如 /user/:name
, 通过嗲用不同的URL来串流不同的那么/
这时候就要获取URL传入的参数和设置接受了
动态路由
1
2
3
4
|
r.GET("/user/:name", func(c *gin.Context) { //设置URL格式
Name := c.Param("name") //获取传入的Name
c.String(http.StatusOK, "Hello,%s!", Name) //输出
})
|
请求服务器:
1
|
curl http://localhost:8888/user/TEST
|
输出结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
StatusCode : 200
StatusDescription : OK
Content : Hello,TEST!
RawContent : HTTP/1.1 200 OK
Content-Length: 11
Content-Type: text/plain; charset=utf-8
Date: Sun, 20 Nov 2022 10:12:20 GMT
Hello,TEST!
Forms : {}
Headers : {[Content-Length, 11], [Content-Type, text/plain; charset=utf-8], [Date, Sun, 20 Nov 2022 10:12:20
GMT]}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : mshtml.HTMLDocumentClass
RawContentLength : 11
|
完整代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package main
import (
"fmt"
"github.com/gin-gonic/gin" "net/http")
func main() {
r := gin.Default()
r.GET("/user/:name", func(c *gin.Context) { //设置URL格式
Name := c.Param("name") //获取传入的Name
c.String(http.StatusOK, "Hello,%s!", Name) //输出
})
r.Run(":8888") // 端口号8888
}
|
获取Query参数
1
2
3
4
5
|
r.GET("/user", func(c *gin.Context) { //设置URL格式
Name := c.Query("name") //获取传入的Name
role := c.DefaultQuery("role", "Teacher") //设置参数默认值 ,不存在参数返回默认值,存在参数就设定为参数
c.String(http.StatusOK, "Hello,%s - %s!", Name, role) //输出
})
|
请求服务器:
1
|
curl http://localhost:8888/user?name=Tom"&"role=Worker
|
需要注意的是, curl中必须给 &
加双引号,否则会被解析成运算符
输出结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
StatusCode : 200
StatusDescription : OK
Content : Hello,Tom - Worker!
RawContent : HTTP/1.1 200 OK
Content-Length: 19
Content-Type: text/plain; charset=utf-8
Date: Sun, 20 Nov 2022 10:24:07 GMT
Hello,Tom - Worker!
Forms : {}
Headers : {[Content-Length, 19], [Content-Type, text/plain; charset=utf-8], [Date, Sun, 20 Nov 2022 10:24:07
GMT]}
Images : {}
InputFields : {}
ParsedHtml : mshtml.HTMLDocumentClass
RawContentLength : 19
|
不带 role 参数请求:
1
|
curl http://localhost:8888/user?name=Tom
|
输出结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
StatusCode : 200
StatusDescription : OK
Content : Hello,Tom - Teacher!
RawContent : HTTP/1.1 200 OK
Content-Length: 20
Content-Type: text/plain; charset=utf-8
Date: Sun, 20 Nov 2022 10:24:19 GMT
Hello,Tom - Teacher!
Forms : {}
Headers : {[Content-Length, 20], [Content-Type, text/plain; charset=utf-8], [Date, Sun, 20 Nov 2022 10:24:19
GMT]}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : mshtml.HTMLDocumentClass
RawContentLength : 20
|
这里看到设置的默认值起作用了.
完整代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package main
import (
"fmt"
"github.com/gin-gonic/gin" "net/http")
func main() {
r := gin.Default()
r.GET("/user", func(c *gin.Context) { //设置URL格式
Name := c.Query("name") //获取传入的Name
role := c.DefaultQuery("role", "Teacher") //设置参数默认值 ,不存在参数返回默认值,存在参数就设定为参数
c.String(http.StatusOK, "Hello,%s - %s!", Name, role) //输出
})
r.Run(":8888") // 端口号8888
}
|
获取POST参数
1
2
3
4
5
6
7
|
r.POST("/form", func(c *gin.Context) {
types := c.DefaultPostForm("type", "post")
username := c.PostForm("username") //获取POST参数中username 的值
password := c.PostForm("password") //设置默认值
c.String(http.StatusOK, fmt.Sprintf("username:%s,password:%s,type:%s", username, password, types))
})
|
请求服务器:
1
|
CURL -X POST -d "username=TEST&password=123123" "http://localhost:8888/form"
|
值得注意的是 这条指令在PowerShell里面会报错
报错为:
1
2
3
4
5
6
|
Invoke-WebRequest : 找不到与参数名称“X”匹配的参数。
所在位置 行:1 字符: 6
+ CURL -X POST -d "username=TEST&password=123123" "http://localhost:888 ...
+ ~~
+ CategoryInfo : InvalidArgument: (:) [Invoke-WebRequest],ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
|
需要将请求改为PowerShell认识的格式:
1
|
curl -Uri 'http://localhost:8888/form' -Body 'username=TEST&password=123123' -Method 'POST'
|
或者可以试试转到CMD进行 CURL操作,或者下一个PostMan .
输出结果:
1
|
username:TEST,password:123123,type:post
|
PowerShell输出结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
StatusCode : 200
StatusDescription : OK
Content : username:TEST,password:123123,type:post
RawContent : HTTP/1.1 200 OK
Content-Length: 39
Content-Type: text/plain; charset=utf-8
Date: Sun, 20 Nov 2022 12:39:09 GMT
username:TEST,password:123123,type:post
Forms : {}
Headers : {[Content-Length, 39], [Content-Type, text/plain; charset=utf-8], [Date, Sun, 20 Nov 2022 12:39:09
GMT]}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : mshtml.HTMLDocumentClass
RawContentLength : 39
|
完整代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package main
import (
"fmt"
"github.com/gin-gonic/gin" "net/http")
func main() {
r := gin.Default()
r.POST("/form", func(c *gin.Context) {
types := c.DefaultPostForm("type", "post")
username := c.PostForm("username") //获取POST参数中username 的值
password := c.PostForm("password") //设置默认值
c.String(http.StatusOK, fmt.Sprintf("username:%s,password:%s,type:%s", username, password, types))
})
r.Run(":8888") // 端口号8888
}
|
Query 和 POST 混合参数
1
2
3
4
5
6
7
8
9
10
|
//Query 和 POST 混合
r.POST("/post", func(c *gin.Context) {
id := c.Query("id") //获取Query传入的ID
name := c.PostForm("name") //获取POST传入的Name
c.JSON(http.StatusOK, gin.H{ //结果返回一个JSON
"ID": id,
"Name": name,
})
})
|
请求服务器:
1
|
CURL -Uri 'http://localhost:8888/post?id=001' -Body 'name=TEST' -Method 'POST'
|
返回结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
StatusCode : 200
StatusDescription : OK
Content : {"ID":"001","Name":"TEST"}
RawContent : HTTP/1.1 200 OK
Content-Length: 26
Content-Type: application/json; charset=utf-8
Date: Mon, 21 Nov 2022 00:20:33 GMT
{"ID":"001","Name":"TEST"}
Forms : {}
Headers : {[Content-Length, 26], [Content-Type, application/json; charset=utf-8], [Date, Mon, 21 Nov 2022 00:
20:33 GMT]}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : mshtml.HTMLDocumentClass
RawContentLength : 26
|
完整代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package main
import (
"fmt"
"github.com/gin-gonic/gin" "net/http")
func main() {
r := gin.Default()
//Query 和 POST 混合
r.POST("/post", func(c *gin.Context) {
id := c.Query("id") //获取Query传入的ID
name := c.PostForm("name") //获取POST传入的Name
c.JSON(http.StatusOK, gin.H{ //结果返回一个JSON
"ID": id,
"Name": name,
})
})
r.Run(":8888") // 端口号8888
}
|
Map参数
1
2
3
4
5
6
7
8
|
//获取MAP参数
r.POST("/map", func(c *gin.Context) {
name := c.QueryMap("name") //创建接收MAP
c.JSON(http.StatusOK, gin.H{ //返回响应JSON
"Name": name,
})
})
|
请求服务器:
1
|
CURL -Uri 'http://localhost:8888/map?name[Jack]=001' -Method 'POST'
|
返回响应:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
StatusCode : 200
StatusDescription : OK
Content : {"Name":{"Jack":"001"}}
RawContent : HTTP/1.1 200 OK
Content-Length: 23
Content-Type: application/json; charset=utf-8
Date: Mon, 21 Nov 2022 00:35:36 GMT
{"Name":{"Jack":"001"}}
Forms : {}
Headers : {[Content-Length, 23], [Content-Type, application/json; charset=utf-8], [Date, Mon, 21 Nov 2022 00:
35:36 GMT]}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : mshtml.HTMLDocumentClass
RawContentLength : 23
|
完整代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package main
import (
"fmt"
"github.com/gin-gonic/gin" "net/http")
func main() {
r := gin.Default()
//获取MAP参数
r.POST("/map", func(c *gin.Context) {
name := c.QueryMap("name") //创建接收MAP
c.JSON(http.StatusOK, gin.H{ //返回响应JSON
"Name": name,
})
})
r.Run(":8888") // 端口号8888
}
|
重定向
1
2
3
4
5
|
//GET请求重定向
r.GET("/redirect", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "https://www.baidu.com/")
})
|
请求服务器:
1
|
CURL "http://localhost:8888/redirect"
|
请求结果:
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
|
StatusCode : 200
StatusDescription : OK
Content : <html>
<head>
<script>
location.replace(location.href.replace("https://","http://"));
</script>
</head>
<body>
<noscript><meta http-equiv="refresh" content="0;url=http://www.baidu.com/"></...
RawContent : HTTP/1.1 200 OK
Connection: keep-alive
Accept-Ranges: bytes
Content-Length: 227
Cache-Control: no-cache
Content-Type: text/html
Date: Mon, 21 Nov 2022 00:54:26 GMT
P3P: CP=" OTI DSP COR IVA OUR...
Forms : {}
Headers : {[Connection, keep-alive], [Accept-Ranges, bytes], [Content-Length, 227], [Cache-Control, no-cache]
...}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : mshtml.HTMLDocumentClass
RawContentLength : 227
|
会发现我们跳转到了Baidu
分组路由 (Grouping Routes)
我们可以将拥有共同前缀URL的路由划分为一个分组路由
1
2
3
4
5
6
7
8
9
10
11
12
|
//分组路由
G := r.Group("/g")
G.POST("/post", func(c *gin.Context) { //POST 请求
name := c.PostForm("name")
c.String(http.StatusOK, fmt.Sprintf("Post Name: %v\n", name))
})
G.GET("/get", func(c *gin.Context) { //GET 请求
c.String(http.StatusOK, fmt.Sprintf("Hello,World!"))
})
|
GET 请求服务器:
1
|
CURL "http://localhost:8888/g/get"
|
响应结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
StatusCode : 200
StatusDescription : OK
Content : Hello,World!
RawContent : HTTP/1.1 200 OK
Content-Length: 12
Content-Type: text/plain; charset=utf-8
Date: Mon, 21 Nov 2022 01:11:33 GMT
Hello,World!
Forms : {}
Headers : {[Content-Length, 12], [Content-Type, text/plain; charset=utf-8], [Date, Mon, 21 Nov 2022 01:11:33
GMT]}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : mshtml.HTMLDocumentClass
RawContentLength : 12
|
POST 请求服务器:
1
|
CURL -Uri "http://localhost:8888/g/post" -Body "name=POST" -Method "POST"
|
响应结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
StatusCode : 200
StatusDescription : OK
Content : Post Name: POST
RawContent : HTTP/1.1 200 OK
Content-Length: 16
Content-Type: text/plain; charset=utf-8
Date: Mon, 21 Nov 2022 01:16:06 GMT
Post Name: POST
Forms : {}
Headers : {[Content-Length, 16], [Content-Type, text/plain; charset=utf-8], [Date, Mon, 21 Nov 2022 01:16:06
GMT]}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : mshtml.HTMLDocumentClass
RawContentLength : 16
|
文件上传
Golang 的 Gin框架还支持文件上传,文件上传需要 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
|
//需要注意的是,GET请求和POST请求需要统一,否则无法上传文件
r.POST("/", func(c *gin.Context) { //接受上传的文件并且保存到本地
file, err := c.FormFile("upload")
if err != nil {
c.String(http.StatusBadRequest, "请求失败 Err: %s", err.Error())
return
}
//获取文件名
fileName := file.Filename
fmt.Println("文件名为: ", fileName) //输出文件名
//将上传的文件保存带本地
err = c.SaveUploadedFile(file, fileName)
if err != nil {
c.String(http.StatusBadRequest, "文件保存失败 Err: %s", err.Error())
return
}
c.String(http.StatusOK, "%s uploaded!", file.Filename)
})
r.LoadHTMLGlob("./index.html") //加上上传的HTML文件.这里也可以改成直接加载文件夹下的文件
r.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", nil)
})
|
HTML模板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Upload.HTML</title>
</head>
<body>
<form action="/" method="post" enctype="multipart/form-data">
<input name="upload" type="file">
<input name="uploadBtn" type="submit" value="上传">
</form>
</body>
</html>
|
需要注意的是,如果你直接使用 Goland 编译的话就会报错找不到 HMTL 文件,这时候需要你找到你写的 Go 文件目录,手动编译文件,然后运行 . (比如我的Go文件名是GinFlierUpdate ,我需要这样手动编译)
1
|
go build GinFlierUpdate.go
|
完整代码
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
28
29
30
31
32
33
34
35
36
37
38
|
package main
import (
"fmt"
"github.com/gin-gonic/gin" "net/http")
func main() {
r := gin.Default()
r.POST("/", func(c *gin.Context) {
file, err := c.FormFile("upload")
if err != nil {
c.String(http.StatusBadRequest, "请求失败 Err: %s", err.Error())
return
}
//获取文件名
fileName := file.Filename
fmt.Println("文件名为: ", fileName) //输出文件名
//将上传的文件保存带本地
err = c.SaveUploadedFile(file, fileName)
if err != nil {
c.String(http.StatusBadRequest, "文件保存失败 Err: %s", err.Error())
return
}
c.String(http.StatusOK, "%s uploaded!", file.Filename)
})
r.LoadHTMLGlob("./index.html")
r.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", nil)
})
r.Run(":8888") // 端口号8888
}
|
多文件上传
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
r.POST("/", func(c *gin.Context) {
form, _ := c.MultipartForm()
files, _ := form.File["upload[]"] //获取文件数组
for _, file := range files { //遍历保存
//获取文件名
fileName := file.Filename
fmt.Println("文件名为: ", fileName) //输出文件名
//将上传的文件保存带本地
err := c.SaveUploadedFile(file, fileName)
if err != nil {
c.String(http.StatusBadRequest, "文件保存失败 Err: %s", err.Error())
return
}
c.String(http.StatusOK, "%s uploaded!\n", file.Filename)
}
})
|
由于 HTML 的 Input标签只能上传一个文件,所以我们要在标签内加上 multiple
属性来保证能上传多个文件
HTML 模板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Upload.HTML</title>
</head>
<body>
<form action="/" method="post" enctype="multipart/form-data">
<input multiple name="upload[]" type="file">
<input name="uploadBtn" type="submit" value="上传" formaction="" >
</form>
</body>
</html>
|
完整代码
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
28
29
30
31
32
33
34
35
36
37
38
39
40
|
package main
import (
"fmt"
"github.com/gin-gonic/gin" "net/http")
func main() {
r := gin.Default()
r.POST("/", func(c *gin.Context) {
form, _ := c.MultipartForm()
files, _ := form.File["upload[]"]
for _, file := range files {
//获取文件名
fileName := file.Filename
fmt.Println("文件名为: ", fileName) //输出文件名
//将上传的文件保存带本地
err := c.SaveUploadedFile(file, fileName)
if err != nil {
c.String(http.StatusBadRequest, "文件保存失败 Err: %s", err.Error())
return
}
c.String(http.StatusOK, "%s uploaded!\n", file.Filename)
}
})
r.LoadHTMLGlob("./index.html")
r.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", nil)
})
r.Run(":8888") // 端口号8888
}
|
中间件 (Middleware)
所谓中间件,就是连接上下级不同功能的函数或者软件,通常进行一些包裹函数的行为,为被包裹函数提供添加一些功能或行为。
JAVA 中的 Filter() 就是起到了中间件的作用.
在 Go 语言中,中间件 Handler 是封装另一个 http.Handler
以对请求进行预处理或后续处理的 http.Handler
。它介于 Go Web 服务器与实际的处理程序之间,因此被称为“中间件”。
使用中间件
中间件的使用相当简单,只要对需要中间件的路由使用 Use()方法即可
实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package main
import (
"github.com/gin-gonic/gin"
"net/http")
func main() {
r := gin.Default()
r.LoadHTMLGlob("html/*")
adminGroup := r.Group("/")
adminGroup.Use(gin.BasicAuth(gin.Accounts{
"admin": "123456",
}))
adminGroup.GET("", func(c *gin.Context) {
c.HTML(http.StatusOK, "background.html", nil)
})
r.Run(":8888")
}
|
HTML
1
2
3
4
5
6
7
8
9
10
|
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>网站后台</title>
</head>
<body>
<h>这里是网站后台</h>
</body>
</html>
|
访问测试
1
|
CURL "http://localhost:8888/"
|
这里看到直接返回404
1
2
3
4
5
6
7
|
CURL : 远程服务器返回错误: (401) 未经授权。
所在位置 行:1 字符: 1
+ CURL "http://localhost:8888/"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest],WebExce
ption
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
|
这就是中间件起到作用了
全局中间件
这个是在服务启动就开始注册,全局意味着所有API接口都会经过这里。Gin的中间件是通过Use
方法设置的,它接收一个可变参数,所以我们同时可以设置多个中间件。
1
2
3
4
5
6
7
|
// 1.创建路由
r := gin.Default()
//默认带Logger(), Recovery()这两个内置中间件
r:= gin.New()
//不带任何中间件
// 注册中间件
r.Use(MiddleWare())
|
gin.Default()默认使用了日志写入中间件 Logger和Recovery中间件
这里新建一个自定义中间件middleware,并且注册成为全局中间件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package main
import (
"fmt"
"github.com/gin-gonic/gin")
func main() {
r := gin.Default()
r.Use(middleware)
r.GET("/", func(c *gin.Context) {
c.Writer.WriteString("GET API 执行")
})
r.Run(":8888")
}
func middleware(c *gin.Context) {
fmt.Println("Middleware执行")
}
|
运行发现请求先到达中间件,然后才到路由的
1
2
3
|
[GIN-debug] Listening and serving HTTP on :8888
Middleware执行
[GIN] 2022/12/03 - 22:53:15 | 200 | 0s | ::1 | GET "/"
|
局部中间件
局部中间件意味着部分接口才会生效,只在局部使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package main
import (
"fmt"
"github.com/gin-gonic/gin")
func main() {
r := gin.Default()
//局部中间件
r.GET("/GET", middleware, func(c *gin.Context) {
c.Writer.WriteString("GET API 执行")
})
r.GET("/", func(c *gin.Context) {
c.Writer.WriteString("GET API 执行")
})
r.Run(":8888")
}
func middleware(c *gin.Context) {
fmt.Println("Middleware执行")
}
|
Gin控制台输出结果:
1
2
3
4
|
[GIN-debug] Listening and serving HTTP on :8888
[GIN] 2022/12/03 - 22:55:30 | 200 | 0s | ::1 | GET "/"
Middleware执行
[GIN] 2022/12/03 - 22:56:25 | 200 | 369.4µs | ::1 | GET "/GET"
|
自定义中间件
https://cloud.tencent.com/developer/article/1585029
数值传递
可以使用gin.Context
中的Set()
方法来将中间件处理过的请求进行传递,而 Set()
通过一个key来存储作何类型的数据,方便下一层获取。
1
|
func (c *Context) Set(key string, value interface{})
|
当我们在中间件中通过Set方法设置一些数值,在下一层中间件或HTTP请求处理方法中,可以使用下面列出的方法通过key获取对应数据。
其中,gin.Context的Get方法返回interface{}
,通过返回exists可以判断key是否存在。
1
|
func (c *Context) Get(key string) (value interface{}, exists bool)
|
当我们确定通过Set方法设置对应数据类型的值时,可以使用下面方法获取应数据类型的值。
1
2
3
4
5
6
7
8
9
10
11
|
func (c *Context) GetBool(key string) (b bool)
func (c *Context) GetDuration(key string) (d time.Duration)
func (c *Context) GetFloat64(key string) (f64 float64)
func (c *Context) GetInt(key string) (i int)
func (c *Context) GetInt64(key string) (i64 int64)
func (c *Context) GetString(key string) (s string)
func (c *Context) GetStringMap(key string) (sm map[string]interface{})
func (c *Context) GetStringMapString(key string) (sms map[string]string)
func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string)
func (c *Context) GetStringSlice(key string) (ss []string)
func (c *Context) GetTime(key string) (t time.Time)
|
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package main
import (
"github.com/gin-gonic/gin"
"strconv")
func main() {
r := gin.Default()
r.GET("/", mw, func(c *gin.Context) {
Num := c.GetInt("Num")
c.Writer.WriteString(strconv.Itoa(Num))
})
r.Run(":8088")
}
func mw(c *gin.Context) {
c.Set("Num", 100)
}
|
访问服务器
1
|
CURL http://localhost:8088/
|
返回响应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
StatusCode : 200
StatusDescription : OK
Content : 100
RawContent : HTTP/1.1 200 OK
Content-Length: 3
Content-Type: text/plain; charset=utf-8
Date: Sat, 10 Dec 2022 10:29:42 GMT
100
Forms : {}
Headers : {[Content-Length, 3], [Content-Type, text/plain; charset=utf-8], [Date, Sat, 10 Dec 2022 10:29:42 G
MT]}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : mshtml.HTMLDocumentClass
RawContentLength : 3
|
拦截请求与后置拦截
拦截请求
当用户请求不合法时,可以使用下面列出的gin.Context
的几个方法中断用户请求:
1
2
3
|
func (c *Context) Abort()
func (c *Context) AbortWithError(code int, err error) *Error
func (c *Context) AbortWithStatus(code int)
|
使用AbortWithStatusJSON()方法,中断用户请求后,则可以返回json格式的数据.
1
|
func (c *Context) AbortWithStatusJSON(code int, jsonObj interface{})
|
后置拦截
当然也可以在中间件处理完请求之后,进行拦截,这时候就要使用 gin.Context
中的 Next()
方法来实现后置拦截.
Next 函数会挂起当前所在的函数,然后调用后面的中间件,待后面中间件执行完毕后,再接着执行当前函数。
Next 函数定义如下:
1
|
func (c *Context) Next()
|
示例
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
28
29
|
package main
import (
"fmt"
"github.com/gin-gonic/gin" "net/http")
func main() {
router := gin.New()
mid1 := func(c *gin.Context) {
fmt.Println("mid1 start")
c.Next()
fmt.Println("mid1 end")
}
mid2 := func(c *gin.Context) {
fmt.Println("mid2 start")
fmt.Println("mid2 end")
}
mid3 := func(c *gin.Context) {
fmt.Println("mid3 start")
fmt.Println("mid3 end")
}
router.Use(mid1, mid2, mid3)
router.GET("/", func(c *gin.Context) {
fmt.Println("process get request")
c.JSON(http.StatusOK, "hello")
})
router.Run(":8088")
}
|
请求
1
|
CURL http://localhost:8088/
|
响应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
StatusCode : 200
StatusDescription : OK
Content : "hello"
RawContent : HTTP/1.1 200 OK
Content-Length: 7
Content-Type: application/json; charset=utf-8
Date: Sat, 10 Dec 2022 10:46:06 GMT
"hello"
Forms : {}
Headers : {[Content-Length, 7], [Content-Type, application/json; charset=utf-8], [Date, Sat, 10 Dec 2022 10:4
6:06 GMT]}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : mshtml.HTMLDocumentClass
RawContentLength : 7
|
控制台输出
1
2
3
4
5
6
7
8
|
[GIN-debug] Listening and serving HTTP on :8088
mid1 start
mid2 start
mid2 end
mid3 start
mid3 end
process get request
mid1 end
|
异常处理