zeromicro / go-zero

A cloud-native Go microservices framework with cli tool for productivity.
https://go-zero.dev
MIT License
29.43k stars 3.97k forks source link

路由中间件支持ServiceContext参数传递 #867

Closed minggaodong closed 3 years ago

minggaodong commented 3 years ago

需求: 想基于路由中间件开发一个公共拦截器,但是拦截器内部需要查询缓存以及微服务接口,由于gozero框架不支持将ServiceContext参数传递给中间件,所以无法实现

建议: 可以支持ServiceContext参数传递给路由中间件,这样路由中间件的功能就不受限制了

feihua commented 3 years ago

局部的能传进去的

minggaodong commented 3 years ago

你意思是使用全局中间件吗,我试了能传进去,但是运行时panic了,代码如下

func main() {
    flag.Parse()

    var c config.Config
    conf.MustLoad(*configFile, &c)

    ctx := svc.NewServiceContext(c)
    server := rest.MustNewServer(c.RestConf)
    defer server.Stop()

    server.Use(func(next http.HandlerFunc) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
            var myctx context.Context
            resp, _ := ctx.Verify.Ping(myctx, &verify.Request{
                Ping: "pingtest",
            })
            w.WriteHeader(200)
            w.Write([]byte(resp.Pong))

            next(w, r)
        }
    })

    handler.RegisterHandlers(server, ctx)

    fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
    server.Start()
}
feihua commented 3 years ago

不是全局的,是局部的,我是这样用的,我之前做权限拦截的时候 ,在登录的时候 用redis缓存起来,然后在局部中间件拿redis里面的值出来对比拦截的

func NewServiceContext(c config.Config) *ServiceContext {
    newRedis := redis.NewRedis(c.Redis.Address, redis.NodeType)
    return &ServiceContext{
        Config:   c,
        CheckUrl: middleware.NewCheckUrlMiddleware(newRedis).Handle,
        Redis:    newRedis,
    }
}

middleware代码如下

func (m *CheckUrlMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {

        //判断请求header中是否携带了x-user-id
        userId := r.Context().Value("userId").(json.Number).String()
        if userId == "" {
            logx.Errorf("缺少必要参数x-user-id")
            httpx.Error(w, errorx.NewDefaultError("缺少必要参数x-user-id"))
            return
        }

        //获取用户能访问的url
        urls, err := m.Redis.Get(userId)
        if err != nil {
            logx.Errorf("用户:%s,获取redis连接异常", userId)
            httpx.Error(w, errorx.NewDefaultError(fmt.Sprintf("用户:%s,获取redis连接异常", userId)))
            return
        }

        if len(strings.TrimSpace(urls)) == 0 {
            logx.Errorf("用户userId: %s,还没有登录", userId)
            httpx.Error(w, errorx.NewDefaultError(fmt.Sprintf("用户userId: %s,还没有登录,请先登录", userId)))
            return
        }

        backUrls := strings.Split(urls, ",")

        b := false
        for _, url := range backUrls {
            if url == r.RequestURI {
                b = true
                break
            }
        }

        if b || r.RequestURI == "/api/sys/user/currentUser" || r.RequestURI == "/api/sys/user/selectAllData" || r.RequestURI == "/api/sys/role/queryMenuByRoleId" {
            logx.Infof("用户userId: %s,访问: %s路径", userId, r.RequestURI)
            next(w, r)
        } else {
            logx.Errorf("用户userId: %s,没有访问: %s路径的权限", userId, r.RequestURI)
            httpx.Error(w, errorx.NewDefaultError(fmt.Sprintf("用户userId: %s,没有访问: %s,路径的的权限,请联系管理员", userId, r.RequestURI)))
            return
        }

    }
}
minggaodong commented 3 years ago

理解了,感谢大佬!