背景

基于bm的handler机制,可以自定义很多middleware(中间件)进行通用的业务处理,比如用户登录鉴权。接下来就以鉴权为例,说明middleware的写法和用法。

写自己的中间件

middleware本质上就是一个handler,接口和方法声明如下代码:

  1. // Handler responds to an HTTP request.
  2. type Handler interface {
  3. ServeHTTP(c *Context)
  4. }
  5. // HandlerFunc http request handler function.
  6. type HandlerFunc func(*Context)
  7. // ServeHTTP calls f(ctx).
  8. func (f HandlerFunc) ServeHTTP(c *Context) {
  9. f(c)
  10. }
  1. 实现了Handler接口,可以作为engine的全局中间件使用:engine.Use(YourHandler)
  2. 声明为HandlerFunc方法,可以作为engine的全局中间件使用:engine.UseFunc(YourHandlerFunc),也可以作为router的局部中间件使用:e.GET("/path", YourHandlerFunc)

简单示例代码如下:

  1. type Demo struct {
  2. Key string
  3. Value string
  4. }
  5. // ServeHTTP implements from Handler interface
  6. func (d *Demo) ServeHTTP(ctx *bm.Context) {
  7. ctx.Set(d.Key, d.Value)
  8. }
  9. e := bm.DefaultServer(nil)
  10. d := &Demo{}
  11. // Handler使用如下:
  12. e.Use(d)
  13. // HandlerFunc使用如下:
  14. e.UseFunc(d.ServeHTTP)
  15. e.GET("/path", d.ServeHTTP)
  16. // 或者只有方法
  17. myHandler := func(ctx *bm.Context) {
  18. // some code
  19. }
  20. e.UseFunc(myHandler)
  21. e.GET("/path", myHandler)

全局中间件

在blademaster的server.go代码中,有以下代码:

  1. func DefaultServer(conf *ServerConfig) *Engine {
  2. engine := NewServer(conf)
  3. engine.Use(Recovery(), Trace(), Logger())
  4. return engine
  5. }

会默认创建一个bm engine,并注册Recovery(), Trace(), Logger()三个middlerware用于全局handler处理,优先级从前到后。如果想要将自定义的middleware注册进全局,可以继续调用Use方法如下:

  1. engine.Use(YourMiddleware())

此方法会将YourMiddleware追加到已有的全局middleware后执行。如果需要全部自定义全局执行的middleware,可以使用NewServer方法创建一个无middleware的engine对象,然后使用engine.Use/UseFunc进行注册。

局部中间件

先来看一段鉴权伪代码示例(auth示例代码位置):

  1. func Example() {
  2. myHandler := func(ctx *bm.Context) {
  3. mid := metadata.Int64(ctx, metadata.Mid)
  4. ctx.JSON(fmt.Sprintf("%d", mid), nil)
  5. }
  6. authn := auth.New(&auth.Config{DisableCSRF: false})
  7. e := bm.DefaultServer(nil)
  8. // "/user"接口必须保证登录用户才能访问,那么我们加入"auth.User"来确保用户鉴权通过,才能进入myHandler进行业务逻辑处理
  9. e.GET("/user", authn.User, myHandler)
  10. // "/guest"接口访客用户就可以访问,但如果登录用户我们需要知道mid,那么我们加入"auth.Guest"来尝试鉴权获取mid,但肯定会继续执行myHandler进行业务逻辑处理
  11. e.GET("/guest", authn.Guest, myHandler)
  12. // "/owner"开头的所有接口,都需要进行登录鉴权才可以被访问,那可以创建一个group并加入"authn.User"
  13. o := e.Group("/owner", authn.User)
  14. o.GET("/info", myHandler) // 该group创建的router不需要再显示的加入"authn.User"
  15. o.POST("/modify", myHandler) // 该group创建的router不需要再显示的加入"authn.User"
  16. e.Start()
  17. }

内置中间件

Recovery

代码位于pkg/net/http/blademaster/recovery.go内,用于recovery panic。会被DefaultServer默认注册,建议使用NewServer的话也将其作为首个中间件注册。

Trace

代码位于pkg/net/http/blademaster/trace.go内,用于trace设置,并且实现了net/http/httptrace的接口,能够收集官方库内的调用栈详情。会被DefaultServer默认注册,建议使用NewServer的话也将其作为第二个中间件注册。

Logger

代码位于pkg/net/http/blademaster/logger.go内,用于请求日志记录。会被DefaultServer默认注册,建议使用NewServer的话也将其作为第三个中间件注册。

CSRF

代码位于pkg/net/http/blademaster/csrf.go内,用于防跨站请求。如要使用如下:

  1. e := bm.DefaultServer(nil)
  2. // 挂载自适应限流中间件到 bm engine,使用默认配置
  3. csrf := bm.CSRF([]string{"bilibili.com"}, []string{"/a/api"})
  4. e.Use(csrf)
  5. // 或者
  6. e.GET("/api", csrf, myHandler)

CORS

代码位于pkg/net/http/blademaster/cors.go内,用于跨域允许请求。请注意该:

  1. 使用该中间件进行全局注册后,可”省略”单独为OPTIONS请求注册路由,如示例一。
  2. 使用该中间单独为某路由注册,需要为该路由再注册一个OPTIONS方法的同路径路由,如示例二。

示例一:

  1. e := bm.DefaultServer(nil)
  2. // 挂载自适应限流中间件到 bm engine,使用默认配置
  3. cors := bm.CORS([]string{"github.com"})
  4. e.Use(cors)
  5. // 该路由可以默认针对 OPTIONS /api 的跨域请求支持
  6. e.POST("/api", myHandler)

示例二:

  1. e := bm.DefaultServer(nil)
  2. // 挂载自适应限流中间件到 bm engine,使用默认配置
  3. cors := bm.CORS([]string{"github.com"})
  4. // e.Use(cors) 不进行全局注册
  5. e.OPTIONS("/api", cors, myHandler) // 需要单独为/api进行OPTIONS方法注册
  6. e.POST("/api", cors, myHandler)

自适应限流

更多关于自适应限流的信息可参考:kratos 自适应限流。如要使用如下:

  1. e := bm.DefaultServer(nil)
  2. // 挂载自适应限流中间件到 bm engine,使用默认配置
  3. limiter := bm.NewRateLimiter(nil)
  4. e.Use(limiter.Limit())
  5. // 或者
  6. e.GET("/api", csrf, myHandler)

扩展阅读

bm快速开始
bm模块说明
bm基于pb生成