中间件使用

在上一节,我们演示了怎么使用jwt鉴权,相信你已经掌握了对jwt的基本使用,本节我们来看一下api服务中间件怎么使用。

中间件分类

在go-zero中,中间件可以分为路由中间件和全局中间件,路由中间件是指某一些特定路由需要实现中间件逻辑,其和jwt类似,没有放在jwt:xxx下的路由不会使用中间件功能, 而全局中间件的服务范围则是整个服务。

中间件使用

这里以search服务为例来演示中间件的使用

路由中间件

  • 重新编写search.api文件,添加middleware声明

    1. $ cd service/search/api
    2. $ vim search.api
    1. type SearchReq struct {}
    2. type SearchReply struct {}
    3. @server(
    4. jwt: Auth
    5. middleware: Example // 路由中间件声明
    6. )
    7. service search-api {
    8. @handler search
    9. get /search/do (SearchReq) returns (SearchReply)
    10. }
  • 重新生成api代码
    1. $ goctl api go -api search.api -dir .
    1. etc/search-api.yaml exists, ignored generation
    2. internal/config/config.go exists, ignored generation
    3. search.go exists, ignored generation
    4. internal/svc/servicecontext.go exists, ignored generation
    5. internal/handler/searchhandler.go exists, ignored generation
    6. internal/handler/pinghandler.go exists, ignored generation
    7. internal/logic/searchlogic.go exists, ignored generation
    8. internal/logic/pinglogic.go exists, ignored generation
    9. Done.
    生成完后会在internal目录下多一个middleware的目录,这里即中间件文件,后续中间件的实现逻辑也在这里编写。
  • 完善资源依赖ServiceContext

    1. $ vim service/search/api/internal/svc/servicecontext.go
    1. type ServiceContext struct {
    2. Config config.Config
    3. Example rest.Middleware
    4. }
    5. func NewServiceContext(c config.Config) *ServiceContext {
    6. return &ServiceContext{
    7. Config: c,
    8. Example: middleware.NewExampleMiddleware().Handle,
    9. }
    10. }
  • 编写中间件逻辑 这里仅添加一行日志,内容example middle,如果服务运行输出example middle则代表中间件使用起来了。

    1. $ vim service/search/api/internal/middleware/examplemiddleware.go
    1. package middleware
    2. import "net/http"
    3. type ExampleMiddleware struct {
    4. }
    5. func NewExampleMiddleware() *ExampleMiddleware {
    6. return &ExampleMiddleware{}
    7. }
    8. func (m *ExampleMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
    9. return func(w http.ResponseWriter, r *http.Request) {
    10. // TODO generate middleware implement function, delete after code implementation
    11. // Passthrough to next handler if need
    12. next(w, r)
    13. }
    14. }
  • 启动服务验证
    1. {"@timestamp":"2021-02-09T11:32:57.931+08","level":"info","content":"example middle"}

全局中间件

通过rest.Server提供的Use方法即可

  1. func main() {
  2. flag.Parse()
  3. var c config.Config
  4. conf.MustLoad(*configFile, &c)
  5. ctx := svc.NewServiceContext(c)
  6. server := rest.MustNewServer(c.RestConf)
  7. defer server.Stop()
  8. // 全局中间件
  9. server.Use(func(next http.HandlerFunc) http.HandlerFunc {
  10. return func(w http.ResponseWriter, r *http.Request) {
  11. logx.Info("global middleware")
  12. next(w, r)
  13. }
  14. })
  15. handler.RegisterHandlers(server, ctx)
  16. fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
  17. server.Start()
  18. }
  1. {"@timestamp":"2021-02-09T11:50:15.388+08","level":"info","content":"global middleware"}

在中间件里调用其它服务

通过闭包的方式把其它服务传递给中间件,示例如下:

  1. // 模拟的其它服务
  2. type AnotherService struct{}
  3. func (s *AnotherService) GetToken() string {
  4. return stringx.Rand()
  5. }
  6. // 常规中间件
  7. func middleware(next http.HandlerFunc) http.HandlerFunc {
  8. return func(w http.ResponseWriter, r *http.Request) {
  9. w.Header().Add("X-Middleware", "static-middleware")
  10. next(w, r)
  11. }
  12. }
  13. // 调用其它服务的中间件
  14. func middlewareWithAnotherService(s *AnotherService) rest.Middleware {
  15. return func(next http.HandlerFunc) http.HandlerFunc {
  16. return func(w http.ResponseWriter, r *http.Request) {
  17. w.Header().Add("X-Middleware", s.GetToken())
  18. next(w, r)
  19. }
  20. }
  21. }

完整代码参考:https://github.com/zeromicro/zero-examples/tree/main/http/middleware