go-zero 中间件
一、中间件介绍
中间件(Middleware)是一个在请求和响应处理之间插入的程序或者函数,它可以用来处理、修改或者监控 HTTP 请求和响应的各个方面。
1.中间件的核心概念
-
请求拦截:中间件能够在请求到达目标处理器之前,对请求进行分析、修改或记录。
-
响应拦截:同样,中间件也可以在响应返回给客户端之前,对响应进行处理,例如添加 headers、压缩响应体、格式化响应数据等。
-
链式调用:多个中间件可以串联在一起,形成一个中间件链,依次处理请求和响应。
2.中间件的用途
中间件可以用于以下多个方面:
-
日志记录:记录 HTTP 请求和响应的详细信息,例如请求路径、方法、状态码等。这有助于进行审计和调试。
-
认证和授权:验证请求是否具有必要的权限,确保用户或系统的身份是否合法。这对于保护敏感接口尤其重要。
-
请求限流:控制访问速率,避免服务被恶意请求或流量洪水淹没。这可以提高系统的稳定性和可用性。
-
跨域资源共享 (CORS):处理浏览器的跨域请求。中间件可以在响应中添加适当的 CORS 头,允许来自不同源的请求。
-
错误处理:捕获处理过程中的错误(如 panic)并生成适当的 HTTP 响应。这样可以确保系统在错误情况下不会崩溃,并能够正常返回相应。
-
数据格式化:统一请求和响应的数据格式,例如将响应数据转换为 JSON 格式,或者对请求中的参数进行验证和转换。
-
国际化 (i18n):处理用户的语言偏好和内容的本地化,使应用能够支持多语言。
3.中间件的工作流程
一般来说,工作流程如下:
- 用户发起请求,请求到达 Web 服务器。
- 中间件链开始处理:
- 第一个中间件接收请求,进行相应的处理(如日志记录)。
- 控制权传递给下一个中间件。
- 这个过程可以一直持续,直到所有中间件处理完请求。
- 最终请求到达目标处理器,处理器生成响应。
- 响应同样会经过中间件链进行处理(如添加 headers)。
- 最终响应返回给用户。
二、go-zero内置中间件介绍
go-zero 提供了一系列内置的中间件, 之前我们使用的JWT鉴权就使用了内置的鉴权管理中间件
帮助我们实现了自动验证Token。
在 go-zero 中内置了如下中间件:
- 鉴权管理中间件 AuthorizeHandler
- 熔断中间件 BreakerHandler
- 内容安全中间件 ContentSecurityHandler
- 解密中间件 CryptionHandler
- 压缩管理中间件 GunzipHandler
- 日志中间件 LogHandler
- ContentLength 管理中间件 MaxBytesHandler
- 限流中间件 MaxConnsHandler
- 指标统计中间件 MetricHandler
- 普罗米修斯指标中间件 PrometheusHandler
- panic 恢复中间件 RecoverHandler
- 负载监控中间件 SheddingHandler
- 超时中间件 TimeoutHandler
- 链路追踪中间件 TraceHandler
这些中间件的具体实现可以去看github.com\zeromicro\go-zero@v1.7.3\rest\handler
目录的内容。
1. 开/关中间件
以上中间件默认启用
, 如果想要关闭其中的中间件,我们可以通过配置文件来控制。
如果你看过RestConf
的代码,就应该能看到,它里面包含了一个Middlewares
中间件的配置结构。
RestConf struct {
service.ServiceConf
Host string `json:",default=0.0.0.0"`
Port int
/*
....
*/
//中间件配置相关的结构体
Middlewares MiddlewaresConf
// TraceIgnorePaths is paths blacklist for trace middleware.
}
我们可以看下它的具体内容,可以看到这个结构体主要用来控制,这些中间件是否启动,默认都是开启状态:
MiddlewaresConf struct {
Trace bool `json:",default=true"`
Log bool `json:",default=true"`
Prometheus bool `json:",default=true"`
MaxConns bool `json:",default=true"`
Breaker bool `json:",default=true"`
Shedding bool `json:",default=true"`
Timeout bool `json:",default=true"`
Recover bool `json:",default=true"`
Metrics bool `json:",default=true"`
MaxBytes bool `json:",default=true"`
Gunzip bool `json:",default=true"`
}
那么如何控制这些中间件是否关闭就很简单了,例如我想关闭普罗米修斯指标中间件
,打开yaml
文件:
Middlewares:
Prometheus: false #把值设置为false
关于内置中间件展示就介绍这么多,后面有机会给大家详细介绍下具体的使用方法。
三、自定义中间件
1.局部中间件
在 go-zero 中,我们通过 api 语言来声明 HTTP 服务,然后通过 goctl 生成 HTTP 服务代码。
type (
RegisterRequest {
//请求体定义了 Username 和Password 字段, 并且都设置了不能为空
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
}
RegisterResponse {
//响应体 定义类一个Message 用来返回结果
Message string `json:"message"`
}
)
type (
LoginRequest {
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
}
LoginResponse {
Token string `json:"token"`
}
)
@server (
group: user // 代表当前 service 代码块下的路由生成代码时都会被放到 user 目录下
prefix: /v1 //定义路由前缀为 "/v1"
middleware: TestMiddleware
)
在上面的例子中,我们声明了一个中间件TestMiddleware
,然后在 @server 中通过 middileware
关键字来声明中间件。
需要说明的,我们这个中间件是局部中间件,仅对当前的server
有效 #EE3F4D
使用以下命令更新代码:
goctl api go --api user.api --dir .
命令执行完后,会在项目中生成middleware
文件夹,我们可以先看下routes.go
文件,可以看到这路由前面帮我加了一个中间件
接下来我们打开middleware
目录下的文件,我们简单的添加一个header信息,修改代码:
func (m *TestMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// TODO generate middleware implement function, delete after code implementation
//我们自定义一个header信息
w.Header().Set("xxxx", "aaaabb")
next(w, r)
}
}
然后把中间件注册到服务中,打开servicecontext.go
文件:
type ServiceContext struct {
Config config.Config
UserModel model.UsersModel
UserRpc user.User
TestMiddleware rest.Middleware //定义中间件
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
UserModel: model.NewUsersModel(sqlx.NewMysql(c.MysqlDB.DbSource)),
UserRpc: user.NewUser(zrpc.MustNewClient(c.UserRpcConf)),
//初始化中间件
TestMiddleware Middleware: middleware.NewTestMiddleware Middleware().Handle,
}
}
运行项目测试
2. 全局中间件
下面我们演示下全局中间件
首先我们创建一个 Middleware 方法:
func GlobalMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
logx.Info("全局中间件")
next(w, r)
}
}
然后将其注册到 go-zero 的 rest 中
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
server := rest.MustNewServer(c.RestConf)
defer server.Stop()
//使用Use 注册中间件
server.Use(middleware.GlobalMiddleware)
ctx := svc.NewServiceContext(c)
handler.RegisterHandlers(server, ctx)
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
server.Start()
}