一、中间件的基本概念
中间件是一个函数,它在http请求处理的生命周期中的某个特定点被调用,可以对请求和响应进行预处理或后处理。中间件的主要功能包括日志记录、身份验证、权限控制、跨域资源共享(cors)、参数处理、错误处理等。
日志记录:记录请求的详细信息,如请求路径、请求体等。
身份验证:验证用户的身份,如检查令牌、cookie等。
权限控制:确定用户是否有权访问特定的资源或执行特定的操作。
跨域资源共享(cors):允许不同域之间的资源共享。
参数处理:提取url中的参数或请求体中的数据。
错误处理:捕获并处理请求处理过程中的错误。
在gin框架中,中间件必须是一个gin.handlerfunc类型的函数。中间件函数接受一个*gin.context参数,并可以选择返回一个gin.handlerfunc。中间件函数的典型结构如下:
func myhandler() gin.handlerfunc {
return func(c *gin.context) {
// 可以通过c.set在请求上下文中设置值,后续的处理函数能够取到该值
c.set("usersession", "userid-1")
//c.next() // 放行,默认就会放行
c.abort() // 拦截,到这里就不会往下执行请求了
fmt.println("handlerfunc-info")
}
}
二、中间件的两个专属方法
1. ctx.next() 继续
在程序进入中间件的时候需要先进行一些处理,然后去 执行核心业务,在执行完核心业务之后再回来执行该中间件。
2. ctx.abort() 中断
在程序进入中间件之后我们进行了一些操作,判断该用户不满足访问这个请求的条件,这个时候我们就需要终止这个请求,不让其继续执行,这个时候就使用到了abort
三、注册中间件
在gin框架中,可以通过全局注册或单个路由中注册,路由组注册的方式来使用中间件。
1、定义一个我们自己的 handlerfunc 如上
2、注册全局路由
所有的请求都会经过这里来处理
全局中间件会被应用到所有的路由上。使用r.use()函数来注册全局中间件。
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
"net/http"
)
// 定义一个中间件函数
func myhandler() gin.handlerfunc {
//返回一个gin.handlerfunc函数
return func(c *gin.context) {
// 可以通过c.set在请求上下文中设置值,后续的处理函数能够取到该值
// func (c *context) set(key string, value any)
c.set("usersession", "userid-1")
//c.next() // 放行,默认就会放行
//c.abort() // 拦截,到这里就不会往下执行请求了 可以通过abort做判定,控制请求的走向
fmt.println("handlerfunc-info")
}
}
func main() {
ginserver := gin.default()
// 注册 全局的 handlerfunc
// func (engine *engine) use(middleware ...handlerfunc) iroutes
// 注意,这里面的参数是我们定义的中间件函数的执行
ginserver.use(myhandler())
// 接收请求
ginserver.get("/test", func(c *gin.context) {
// 从上下文取值 拿到我们在中间件中设置的值
name := c.mustget("usersession").(string)
log.println(name)
c.json(http.statusok, gin.h{
"myname": name,
})
})
//再加个post请求,验证全局中间件
ginserver.post("/test", func(c *gin.context) {
namepost := c.mustget("usersession").(string)
log.println(namepost)
c.json(http.statusok, gin.h{
"myname": namepost,
})
})
err := ginserver.run()
if err != nil {
return
}
}
get请求拿到数据

post请求也拿到数据

3、为某个路由单独注册
为单个路由注册的中间件,只能在该路由中生效
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
"net/http"
)
// 定义一个中间件函数
func myhandler() gin.handlerfunc {
//返回一个gin.handlerfunc函数
return func(c *gin.context) {
// 可以通过c.set在请求上下文中设置值,后续的处理函数能够取到该值
// func (c *context) set(key string, value any)
c.set("usersession", "userid-1")
//c.next() // 放行,默认就会放行
//c.abort() // 拦截,到这里就不会往下执行请求了 可以通过abort做判定,控制请求的走向
fmt.println("handlerfunc-info")
}
}
func main() {
ginserver := gin.default()
// 单路由注册中间件 ,只能在该路由中生效
ginserver.get("/test", myhandler(), func(c *gin.context) {
// 从上下文取值 拿到我们在中间件中设置的值
name := c.mustget("usersession").(string)
log.println(name)
c.json(http.statusok, gin.h{
"myname": name,
})
})
//再加个post请求,post请求没有注册中间件,中间件在该路由中不生效
ginserver.post("/test", func(c *gin.context) {
//当没有在该路由中注册中间件时,获取中间件中的变量值,获取不到会报panic
namepost := c.mustget("usersession").(string)
log.println(namepost)
c.json(http.statusok, gin.h{
"myname": namepost,
})
})
err := ginserver.run()
if err != nil {
return
}
}

当没有在该路由中注册中间件时,获取中间件中的变量值,获取不到会报panic

拿不到值报异常

4、为路由组注册中间件
路由组中间件只会被应用到该路由组下的路由上。通过创建一个路由组,并使用v1.use()函数为该路由组注册中间件。
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
"net/http"
)
// 定义一个中间件函数
func myhandler() gin.handlerfunc {
//返回一个gin.handlerfunc函数
return func(c *gin.context) {
// 可以通过c.set在请求上下文中设置值,后续的处理函数能够取到该值
// func (c *context) set(key string, value any)
c.set("usersession", "userid-1")
//c.next() // 放行,默认就会放行
//c.abort() // 拦截,到这里就不会往下执行请求了 可以通过abort做判定,控制请求的走向
fmt.println("handlerfunc-info")
}
}
func main() {
ginserver := gin.default()
//创建路由组
v1 := ginserver.group("/v1") // 创建一个名为/v1的路由组
//路由组应用中间件
v1.use(myhandler())
// 路由组中间件
{
v1.get("/test", func(c *gin.context) {
// 从上下文取值 拿到我们在中间件中设置的值
name := c.mustget("usersession").(string)
log.println(name)
c.json(http.statusok, gin.h{
"myname": name,
})
})
//再加个post请求,post请求没有注册中间件,中间件在该路由中不生效
v1.post("/test", func(c *gin.context) {
//当没有在该路由中注册中间件时,获取中间件中的变量值,获取不到会报panic
namepost := c.mustget("usersession").(string)
log.println(namepost)
c.json(http.statusok, gin.h{
"myname": namepost,
})
})
}
err := ginserver.run()
if err != nil {
return
}
}
在上述代码中,我们创建了一个名为/v1的路由组,并为这个路由组注册了一个中间件。这个中间件只会被应用到/v1下的路由上。
路由组中间件也可以直接应用在group方法中
v1 := r.group("/test", myhandler())
{
shopgroup.get("/index", func(c *gin.context) {...})
...
}
5、gin默认中间件
gin.default()默认使用了logger和recovery中间件,其中:
logger中间件将日志写入gin.defaultwriter,即使配置了gin_mode=release。recovery中间件会recover任何panic。如果有panic的话,会写入500响应码。
如果不想使用上面两个默认的中间件,可以使用gin.new()新建一个没有任何默认中间件的路由。
三、实际应用案例
以下将通过几个实际案例来演示gin框架中间件的用法。
1. 日志记录中间件
日志记录中间件用于记录请求的详细信息,如请求路径、请求体、响应状态码等。
package middleware
import (
"github.com/gin-gonic/gin"
"log"
"time"
)
// loggermiddleware 是一个日志记录中间件
func loggermiddleware() gin.handlerfunc {
return func(c *gin.context) {
// 记录请求开始时的信息
starttime := time.now()
// 调用后续的处理函数
c.next()
// 记录请求结束时的信息
endtime := time.now()
latency := endtime.sub(starttime)
// 记录请求的ip地址和端口号
ipaddress := c.clientip()
// 记录请求的url
requesturl := c.request.url.path
// 记录请求的方法
httpmethod := c.request.method
// 记录请求的状态码
statuscode := c.writer.status()
// 记录日志信息
log.printf("request from %s to %s took %s with method %s and status code %d\n", ipaddress, requesturl, latency, httpmethod, statuscode)
}
}
在main.go中使用该中间件:
package main
import (
"github.com/gin-gonic/gin"
"jingtian/jt_gin/middleware"
"net/http"
)
func main() {
r := gin.default() // 创建一个默认的gin引擎
// 注册中间件
r.use(middleware.loggermiddleware())
// 定义路由
r.get("/test", func(c *gin.context) {
c.json(http.statusok, gin.h{"message": "hello, world!"})
})
err := r.run(":8080")
if err != nil {
return
} // 启动服务器
}
请求

可以看到日志中间件打印出的日志

2. 身份验证中间件
身份验证中间件用于验证用户的身份,如检查令牌、cookie等。
package middleware
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
// authmiddleware 是一个身份验证中间件
func authmiddleware() gin.handlerfunc {
return func(c *gin.context) {
// 假设我们使用bearer令牌进行身份验证
token := c.getheader("authorization")
//查看token
fmt.println("查看token:", token)
if token != "bearer your_token_here" {
//认证未通过,返回401状态码和错误消息
c.json(http.statusunauthorized, gin.h{"error": "unauthorized access"})
c.abort()
return
}
// 调用后续的处理函数
c.next()
}
}
在main.go中使用该中间件:
package main
import (
"github.com/gin-gonic/gin"
"jingtian/jt_gin/middleware"
"net/http"
)
func main() {
r := gin.default() // 创建一个默认的gin引擎
// 注册中间件
r.use(middleware.authmiddleware())
// 定义路由
r.get("/", func(c *gin.context) {
c.json(http.statusok, gin.h{"message": "hello, authenticated user!"})
})
r.run(":8080") // 启动服务器
}

3. 限流中间件
限流中间件用于限制特定时间段内允许的请求数量,以防止服务器过载。
package main
import (
"github.com/gin-gonic/gin"
"time"
)
var (
limiter = newlimiter(10, 1*time.minute) // 设置限流器,允许每分钟最多请求10次
)
// newlimiter 创建限流器
func newlimiter(limit int, duration time.duration) *limiter {
return &limiter{
limit: limit,
duration: duration,
timestamps: make(map[string][]int64),
}
}
// limiter 限流器
type limiter struct {
limit int // 限制的请求数量
duration time.duration // 时间窗口
timestamps map[string][]int64 // 请求的时间戳
}
// middleware 限流中间件
func (l *limiter) middleware(c *gin.context) {
ip := c.clientip() // 获取客户端ip地址
// 检查请求时间戳切片是否存在
if _, ok := l.timestamps[ip]; !ok {
l.timestamps[ip] = make([]int64, 0)
}
now := time.now().unix() // 当前时间戳
// 移除过期的请求时间戳
for i := 0; i < len(l.timestamps[ip]); i++ {
if l.timestamps[ip][i] < now-int64(l.duration.seconds()) {
l.timestamps[ip] = append(l.timestamps[ip][:i], l.timestamps[ip][i+1:]...)
i--
}
}
// 检查请求数量是否超过限制
if len(l.timestamps[ip]) >= l.limit {
c.json(429, gin.h{
"message": "too many requests",
})
c.abort()
return
}
// 添加当前请求时间戳到切片
l.timestamps[ip] = append(l.timestamps[ip], now)
// 继续处理请求
c.next()
}
func main() {
r := gin.default()
// 使用限流中间件
r.use(limiter.middleware)
r.get("/", func(c *gin.context) {
c.json(200, gin.h{
"message": "hello world",
})
})
r.run(":8080")
}
每分钟内访问超过10次,就限流了

以上就是golang gin框架中间件的用法详解的详细内容,更多关于golang gin框架中间件的资料请关注代码网其它相关文章!
发表评论