B站微服务框架Kratos详细教程(3)- 中间件


加粗对吧
紧急呼叫 2022-09-21 09:15:13 62046
分类专栏: 资讯

背景

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

创建新项目

kratos new middledemo --http
gine, 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
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

Ctrl+点击bm.DefaultServer,查看blademaster/server.go代码(注意是kratos框架blademasterserver.go):

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

会默认创建一个bm engine,并注册Recovery(), Trace(), Logger()三个middlerware用于全局handler处理,优先级从前到后。

自定义中间件

如果想要将自定义的middleware注册进全局,可以继续调用Use方法如下:

engine.Use(MyMiddleware())
  • 1

此方法会将MyMiddleware追加到已有的全局middleware后执行。
middleware本质上就是一个handler,接口和方法声明如下:

// 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)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

只要实现了Handler接口,就可以作为engine的全局中间件使用:

engine.Use(YourHandler)
  • 1

声明为HandlerFunc方法,可以作为engine的全局中间件使用:

engine.UseFunc(YourHandlerFunc)
  • 1

也可以作为router的局部中间件使用:

e.GET("/path", YourHandlerFunc)
  • 1

示例代码:

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()
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98

运行代码:

kratos run
  • 1

打开浏览器访问:

http://localhost:8000/middledemo/start
  • 1

查看控制台输出:
中间件输出
可以看到我们自定义的中间件被按顺序执行了一次。

如果需要全部自定义全局执行的middleware,可以使用NewServer方法创建一个无middlewareengine对象,然后使用engine.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
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

局部中间件

先来看一段鉴权伪代码示例:

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()
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

完整示例代码:
首先复制示例代码kratos/example/blademaster/middleware/auth/auth.go到自己的项目,或者可以直接import引入示例代码

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()
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133

打开浏览器分别访问以下地址:

http://localhost:8000/userinfo
http://localhost:8000/userinfo?access_token

网站声明:如果转载,请联系本站管理员。否则一切后果自行承担。

本文链接:https://www.xckfsq.com/news/show.html?id=8714
赞同 0
评论 0 条
紧急呼叫L0
粉丝 0 发表 3 + 关注 私信
上周热门
Kingbase用户权限管理  2033
信刻全自动光盘摆渡系统  1763
信刻国产化智能光盘柜管理系统  1433
银河麒麟添加网络打印机时,出现“client-error-not-possible”错误提示  1034
银河麒麟打印带有图像的文档时出错  940
银河麒麟添加打印机时,出现“server-error-internal-error”  727
麒麟系统也能完整体验微信啦!  669
统信桌面专业版【如何查询系统安装时间】  648
统信操作系统各版本介绍  640
统信桌面专业版【全盘安装UOS系统】介绍  611
本周热议
我的信创开放社区兼职赚钱历程 40
今天你签到了吗? 27
信创开放社区邀请他人注册的具体步骤如下 15
如何玩转信创开放社区—从小白进阶到专家 15
方德桌面操作系统 14
我有15积分有什么用? 13
用抖音玩法闯信创开放社区——用平台宣传企业产品服务 13
如何让你先人一步获得悬赏问题信息?(创作者必看) 12
2024中国信创产业发展大会暨中国信息科技创新与应用博览会 9
中央国家机关政府采购中心:应当将CPU、操作系统符合安全可靠测评要求纳入采购需求 8

添加我为好友,拉您入交流群!

请使用微信扫一扫!