背景
handlermiddlewaremiddleware
创建新项目
kratos new middledemo --http
全局中间件
查看项目bm初始化服务代码:
// New new a bm server.
func New(s pb.DemoServer) (engine *bm.Engine, err error) {
var (
cfg bm.ServerConfig
ct paladin.TOML
)
if err = paladin.Get("http.toml").Unmarshal(&ct); err != nil {
return
}
if err = ct.Get("Server").UnmarshalTOML(&cfg); err != nil {
return
}
svc = s
engine = bm.DefaultServer(&cfg) //Ctrl+点击,查看bm server代码
pb.RegisterDemoBMServer(engine, s)
initRouter(engine)
err = engine.Start()
return
}
blademaster/server.goblademasterserver.go
func DefaultServer(conf *ServerConfig) *Engine {
engine := NewServer(conf)
engine.Use(Recovery(), Trace(), Logger())
return engine
}
bm engineRecovery()Trace()Logger()middlerwarehandler
自定义中间件
middlewareUse
engine.Use(MyMiddleware())
MyMiddlewaremiddlewaremiddlewarehandler
// Handler responds to an HTTP request.
type Handler interface {
ServeHTTP(c *Context)
}
// HandlerFunc http request handler function.
type HandlerFunc func(*Context)
// ServeHTTP calls f(ctx).
func (f HandlerFunc) ServeHTTP(c *Context) {
f(c)
}
Handlerengine
engine.Use(YourHandler)
HandlerFunc
engine.UseFunc(YourHandlerFunc)
也可以作为router的局部中间件使用:
e.GET("/path", YourHandlerFunc)
示例代码:
package http
import (
"net/http"
pb "middledemo/api"
"middledemo/internal/model"
"github.com/go-kratos/kratos/pkg/conf/paladin"
"github.com/go-kratos/kratos/pkg/log"
bm "github.com/go-kratos/kratos/pkg/net/http/blademaster"
)
var svc pb.DemoServer
// New new a bm server.
func New(s pb.DemoServer) (engine *bm.Engine, err error) {
var (
cfg bm.ServerConfig
ct paladin.TOML
)
if err = paladin.Get("http.toml").Unmarshal(&ct); err != nil {
return
}
if err = ct.Get("Server").UnmarshalTOML(&cfg); err != nil {
return
}
svc = s
engine = bm.DefaultServer(&cfg)
myware := &MyMiddleware{}
engine.Use(myware) //注册自定义全局中间件
//engine.UseFunc(myware.ServeHTTP)
//engine.GET("/path",myware.ServeHTTP)
//只有方法的中间件,这个和howToStart一样,只不过在这里是全局的
engine.UseFunc(MyMiddleHandler)
//也可以像howToStart指定范围使用
//engine.GET("/path",MyMiddleHandler)
pb.RegisterDemoBMServer(engine, s)
initRouter(engine)
err = engine.Start()
return
}
func initRouter(e *bm.Engine) {
e.Ping(ping)
g := e.Group("/middledemo", MyGroupHandler)
{
g.GET("/start", howToStart)
}
}
func ping(ctx *bm.Context) {
if _, err := svc.Ping(ctx, nil); err != nil {
log.Error("ping error(%v)", err)
ctx.AbortWithStatus(http.StatusServiceUnavailable)
}
}
// example for http request handler.
func howToStart(c *bm.Context) {
log.Info("输出 Handler: howToStart")
k := &model.Kratos{
Hello: "Golang 大法好 !!!",
}
c.JSON(k, nil)
}
//自定义中间件
type MyMiddleware struct {
Key string
Value string
}
// ServeHTTP implements from Handler interface
func (d *MyMiddleware) ServeHTTP(c *bm.Context) {
c.Set(d.Key, d.Value)
log.Info("全局自定义中间件 MyMiddleware: ServeHTTP")
//TODO: 中间件业务代码, 例如授权验证、限流等操作
c.Next()
}
func MyMiddleHandler(c *bm.Context) {
// some code
log.Info("全局方法中间件: MyMiddleHandler")
c.Next()
}
func MyGroupHandler(c *bm.Context) {
// some code
log.Info("分组中间件: MyGroupHandler")
c.Next()
}
运行代码:
kratos run
打开浏览器访问:
http://localhost:8000/middledemo/start
查看控制台输出:
可以看到我们自定义的中间件被按顺序执行了一次。
middlewareNewServermiddlewareengineengine.Use/UseFunc
示例代码:
// New new a bm server.
func New(s pb.DemoServer) (engine *bm.Engine, err error) {
var (
cfg bm.ServerConfig
ct paladin.TOML
)
if err = paladin.Get("http.toml").Unmarshal(&ct); err != nil {
return
}
if err = ct.Get("Server").UnmarshalTOML(&cfg); err != nil {
return
}
svc = s
//全局自定义, 不使用框架内置中间件: Recovery(), Trace(), Logger()
engine = bm.NewServer(&cfg)
myware := &MyMiddleware{}
engine.Use(myware) //注册自定义全局中间件
//engine.UseFunc(myware.ServeHTTP)
//engine.GET("/path",myware.ServeHTTP)
//只有方法的中间件,这个和howToStart一样,只不过在这里是全局的
engine.UseFunc(MyMiddleHandler)
//也可以像howToStart指定范围使用
//engine.GET("/path",MyMiddleHandler)
pb.RegisterDemoBMServer(engine, s)
initRouter(engine)
err = engine.Start()
return
}
局部中间件
先来看一段鉴权伪代码示例:
func Example() {
myHandler := func(ctx *bm.Context) {
mid := metadata.Int64(ctx, metadata.Mid)
ctx.JSON(fmt.Sprintf("%d", mid), nil)
}
authn := auth.New(&auth.Config{DisableCSRF: false})
e := bm.DefaultServer(nil)
// "/user"接口必须保证登录用户才能访问,那么我们加入"auth.User"来确保用户鉴权通过,才能进入myHandler进行业务逻辑处理
e.GET("/user", authn.User, myHandler)
// "/guest"接口访客用户就可以访问,但如果登录用户我们需要知道mid,那么我们加入"auth.Guest"来尝试鉴权获取mid,但肯定会继续执行myHandler进行业务逻辑处理
e.GET("/guest", authn.Guest, myHandler)
// "/owner"开头的所有接口,都需要进行登录鉴权才可以被访问,那可以创建一个group并加入"authn.User"
o := e.Group("/owner", authn.User)
o.GET("/info", myHandler) // 该group创建的router不需要再显示的加入"authn.User"
o.POST("/modify", myHandler) // 该group创建的router不需要再显示的加入"authn.User"
e.Start()
}
kratos/example/blademaster/middleware/auth/auth.go
package http
import (
"middledemo/internal/server/auth"
"net/http"
pb "middledemo/api"
"middledemo/internal/model"
"github.com/go-kratos/kratos/pkg/conf/paladin"
"github.com/go-kratos/kratos/pkg/log"
bm "github.com/go-kratos/kratos/pkg/net/http/blademaster"
)
var svc pb.DemoServer
// New new a bm server.
func New(s pb.DemoServer) (engine *bm.Engine, err error) {
var (
cfg bm.ServerConfig
ct paladin.TOML
)
if err = paladin.Get("http.toml").Unmarshal(&ct); err != nil {
return
}
if err = ct.Get("Server").UnmarshalTOML(&cfg); err != nil {
return
}
svc = s
engine = bm.DefaultServer(&cfg)
//engine = bm.NewServer(&cfg) //全局自定义, 不使用框架内置中间件: Recovery(), Trace(), Logger()
myware := &MyMiddleware{}
engine.Use(myware) //注册自定义全局中间件
//engine.UseFunc(myware.ServeHTTP)
//engine.GET("/path",myware.ServeHTTP)
//只有方法的中间件,这个和howToStart一样,只不过在这里是全局的
engine.UseFunc(MyMiddleHandler)
//也可以像howToStart指定范围使用
//engine.GET("/path",MyMiddleHandler)
pb.RegisterDemoBMServer(engine, s)
initRouter(engine)
err = engine.Start()
return
}
func initRouter(e *bm.Engine) {
e.Ping(ping)
//按分组验证
g := e.Group("/middledemo", MyGroupHandler)
{
g.GET("/start", howToStart)
}
authn := auth.New(&auth.Config{DisableCSRF: false})
//添加多个Handler,按顺序执行
e.GET("/userinfo", authn.User, userInfo)
//按分组验证
g2 := e.Group("/user", authn.User)
{
g2.GET("/info", userInfo2)
}
}
func ping(ctx *bm.Context) {
if _, err := svc.Ping(ctx, nil); err != nil {
log.Error("ping error(%v)", err)
ctx.AbortWithStatus(http.StatusServiceUnavailable)
}
}
// example for http request handler.
func howToStart(c *bm.Context) {
log.Info("输出 Handler: howToStart")
k := &model.Kratos{
Hello: "Golang 大法好 !!!",
}
c.JSON(k, nil)
}
func userInfo(c *bm.Context) {
log.Info("输出 Handler: userInfo")
k := &model.Kratos{
Hello: "用户信息 !!!",
}
c.JSON(k, nil)
}
func userInfo2(c *bm.Context) {
log.Info("输出 Handler: userInfo")
k := &model.Kratos{
Hello: "用户信息22222 !!!",
}
c.JSON(k, nil)
}
//自定义中间件
type MyMiddleware struct {
Key string
Value string
}
// ServeHTTP implements from Handler interface
func (d *MyMiddleware) ServeHTTP(c *bm.Context) {
c.Set(d.Key, d.Value)
log.Info("全局自定义中间件 MyMiddleware: ServeHTTP")
//TODO: 中间件业务代码, 例如授权验证、限流等操作
c.Next()
}
func MyMiddleHandler(c *bm.Context) {
// some code
log.Info("全局方法中间件: MyMiddleHandler")
c.Next()
}
func MyGroupHandler(c *bm.Context) {
// some code
log.Info("分组中间件: MyGroupHandler")
c.Next()
}
打开浏览器分别访问以下地址:
http://localhost:8000/userinfo
http://localhost:8000/userinfo?access_token=aaaaaaaa
http://localhost:8000/user/info
http://localhost:8000/user/info?access_token=aaaaaaaa
查看控制台输出:
可以看到,没有带上access_token的被拒绝服务
内置中间件
Recovery
pkg/net/http/blademaster/recovery.gorecovery panicDefaultServerNewServer
Trace
pkg/net/http/blademaster/trace.gonet/http/httptraceNewServer
Logger
pkg/net/http/blademaster/logger.goDefaultServerNewServer
CSRF
pkg/net/http/blademaster/csrf.go
e := bm.DefaultServer(nil)
// 挂载自适应限流中间件到 bm engine,使用默认配置
csrf := bm.CSRF([]string{"bilibili.com"}, []string{"/a/api"})
e.Use(csrf)
// 或者
e.GET("/api", csrf, myHandler)
CORS
pkg/net/http/blademaster/cors.go
OPTIONSOPTIONS
e := bm.DefaultServer(nil)
// 挂载自适应限流中间件到 bm engine,使用默认配置
cors := bm.CORS([]string{"github.com"})
e.Use(cors)
// 该路由可以默认针对 OPTIONS /api 的跨域请求支持
e.POST("/api", myHandler)
示例二:
e := bm.DefaultServer(nil)
// 挂载自适应限流中间件到 bm engine,使用默认配置
cors := bm.CORS([]string{"github.com"})
// e.Use(cors) 不进行全局注册
e.OPTIONS("/api", cors, myHandler) // 需要单独为/api进行OPTIONS方法注册
e.POST("/api", cors, myHandler)
自适应限流
更多关于自适应限流的信息可参考:kratos 自适应限流。如要使用如下:
e := bm.DefaultServer(nil)
// 挂载自适应限流中间件到 bm engine,使用默认配置
limiter := bm.NewRateLimiter(nil)
e.Use(limiter.Limit())
// 或者
e.GET("/api", csrf, myHandler)