背景

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

创建新项目

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}

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

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

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

自定义中间件

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

engine.Use(MyMiddleware())

此方法会将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)}

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

engine.Use(YourHandler)

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

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 interfacefunc (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

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

如果需要全部自定义全局执行的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}

局部中间件

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

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到自己的项目,或者可以直接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 interfacefunc (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/userinfohttp://localhost:8000/userinfo?access_token=aaaaaaaahttp://localhost:8000/user/infohttp://localhost:8000/user/info?access_token=aaaaaaaa

查看控制台输出:
可以看到,没有带上access_token的被拒绝服务

内置中间件

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内,用于防跨站请求。如要使用如下:

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内,用于跨域允许请求。请注意该:

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

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)

上下定高 中间自适应_B站微服务框架Kratos详细教程(3)中间件相关推荐

  1. B站微服务框架Kratos详细教程(1)- 安装搭建

    Kratos Kratos是bilibili开源的一套Go微服务框架,包含大量微服务相关框架及工具. 名字来源于:<战神>游戏以希腊神话为背景,讲述由凡人成为战神的奎托斯(Kratos)成 ...

  2. http get请求相同的key_B站微服务框架Kratos详细教程(2)HTTP服务

    背景 在像微服务这样的分布式架构中,经常会有一些需求需要你调用多个服务,但是还需要确保服务的安全性.统一化每次的 请求日志或者追踪用户完整的行为等等. 你可能需要一个框架来帮助你实现这些功能.比如说帮 ...

  3. Spring Boot如何在最短时间里快速搭建微服务框架,详细教程贡上

    前言: Spring Boot是为了简化Spring应用的创建.运行.调试.部署等而出现的,使用它可以做到专注于Spring应用的开发,而无需过多关注XML的配置. 简单来说,它提供了一堆依赖打包,并 ...

  4. 技术研究院006---B站自用的微服务框架——Kratos

    大家都知道微服务有两个痛点,一个是如何拆分微服务,微服务的边界怎么划分制定:二是微服务上了规模之后如何管理,因为只要上了规模,任何小小的问题都可能会被放大,最后导致雪崩效应. Bilibili作为一个 ...

  5. 微服务Springcloud超详细教程+实战(二)

    微服务Springcloud超详细教程+实战(二) -------------------------------------- 远程调用方式 无论是微服务还是分布式服务(都是SOA,都是面向服务编程 ...

  6. go微服务框架Kratos简单使用总结

    Kratos是B站开源的一款go的微服务框架,最近PS5上的 战神·诸神黄昏比较火,主角就是奎托斯.这个框架的名字就取自他. 在进行框架选型时,对比了目前主流的很多go微服务框架,如Zero,最后对比 ...

  7. 初识golang微服务框架kratos

    前言 今天给大家介绍一下Kratos,Kratos 一套轻量级 Go 微服务框架,包含大量微服务相关框架及工具,使用Kratos的原因主要是感觉原来使用的go-kit工具并不是很方便,期望用上krat ...

  8. 上下定高 中间自适应_ADAS|驾驶辅助系统之自适应灯光照明系统

    车灯是汽车的关键部位之一.汽车灯光的影响已经成为与道路安全驾驶密切相关的原因之一.AFS是近年来发展起来的一种新型智能照明系统,它使照明的有效覆盖范围更广,增加了汽车的安全性.随着道路和天气条件的变化 ...

  9. 上下定高 中间自适应_css经典布局——头尾固定高度中间高度自适应布局

    今天说说一个经典布局:头尾固定高度中间高度自适应布局! 相信做过后台管理界面的同学,都非常清楚这个布局.最直观的方式是框架这个我不想多写费话,因为我们的重心不在这里.如果有不了解的同学可以百度.goo ...

  10. 上下定高 中间自适应_联合首发|医药供应链平台一块医药获新一轮融资,赋能产业上下游提质增效...

    创业邦获悉,近日,致力于医药供应链领域的「一块医药」刚刚完成新一轮融资,本轮融资由红杉中国种子基金领投,老股东心元资本.德迅投资.梅花天使创投跟投.联合创始人&CFO何雨表示,本轮融资将主要用 ...

最新文章

  1. mysql 的connect 设置 无法点next_技术分享 | MySQL 使用 MariaDB 审计插件
  2. SQL Server 获取表或视图结构信息
  3. 病毒:101种令人难以置信的微生物的图解指南 Virus: An Illustrated Guide to 101 Incredible Microbes PDF
  4. 干货 | 一文概览主要语义分割网络,FCN、UNet、SegNet、DeepLab 等等等等应有尽有
  5. 序列化和反序列化的概述
  6. 利用geogle中memory工具分析js占用内存
  7. java 安全库_国家信息安全漏洞库
  8. Web请求中同步与异步的区别
  9. 怎么查jupyter lab 内核_抗氧化精华推荐 CHA:LAB诗蕾泊帮你告别“零点肌”|抗氧化|精华-综合资讯...
  10. 进程的调度策略与进程的状态
  11. CVPR2022 | 移动端手部三维重建
  12. Android 国内集成使用谷歌地图
  13. Linux:搭建GIT服务,Linux中使用git,git基础命令,和原理
  14. iOS开发中常见的英文
  15. 云数据库ClickHouse资源隔离 - 弹性资源队列
  16. Linux之编写shell脚本
  17. 【天祺围棋】围棋分段位【打谱】【猜局】提高法
  18. 辰视智能受邀参加2021成都国际工业博览会
  19. python检测网页能否访问
  20. DFRobot柔性非接触式液位传感器的工作原理和应用领域

热门文章

  1. javascript-----日历控件
  2. mac修改了/etc/profile导致所有命令不能用、mac下vim不能使用
  3. ActiveMQ(二)——常用api、持久化、签收模式、以及常见问题
  4. 【Mybatis】mybatis的小细节
  5. MySQL中的时间函数
  6. Nginx的入门详解
  7. webpack系列之-原理篇
  8. 药品缺陷检测中的机器视觉技术
  9. 《软件需求十步走》阅读笔记6
  10. hasOwnProperty()方法与in操作符