中间件中的 gorilla/mux 下一个处理程序是 nil

Posted

技术标签:

【中文标题】中间件中的 gorilla/mux 下一个处理程序是 nil【英文标题】:gorilla/mux next handler in middleware is nil 【发布时间】:2021-09-13 05:57:35 【问题描述】:

我有一个自定义的中间件,我给中间件传递了一个参数,但是当我启动服务器时,我得到了一个NPE

func SetMigrater(mgt dmigrate.Migrater) mux.MiddlewareFunc 
    return func(next http.Handler) http.Handler 
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) 
            r = httputil.WithValue(r, dmigrate.DockerMigraterKey, mgt)
            next.ServeHTTP(w, r)  // next is nil ! panic here. middleware/inject.go:57
        )
    

RequestID 中间件:

14  func RequestID(next http.Handler) http.Handler 
15      return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) 
16          id := uuidutil.GenerateWithoutHyphen()
17          r = r.WithContext(context.WithValue(r.Context(), RequestIdKey, id))
18          w.Header().Add(RequestIdKey, id)
19          next.ServeHTTP(w, r)
20      )
21  

堆栈跟踪:

2021/09/13 10:39:54 runtime error: invalid memory address or nil pointer dereference
2021/09/13 10:39:54 goroutine 502 [running]:
runtime/debug.Stack(0xc001008aa0, 0x1, 0x1)
    /usr/local/go/src/runtime/debug/stack.go:24 +0x9f
github.com/gorilla/handlers.recoveryHandler.log(0x2210e60, 0xc00100e7e0, 0x0, 0x0, 0x1, 0xc001008aa0, 0x1, 0x1)
    /app/vendor/github.com/gorilla/handlers/recovery.go:89 +0x7e
github.com/gorilla/handlers.recoveryHandler.ServeHTTP.func1(0x22316e8, 0xc00100b680, 0x2210e60, 0xc00100e7e0, 0x0, 0x0, 0x1)
    /app/vendor/github.com/gorilla/handlers/recovery.go:74 +0xe5
panic(0x1b59240, 0x2f8e960)
    /usr/local/go/src/runtime/panic.go:965 +0x1b9
.../server/http/handlers/middleware.SetMigrater.func1.1(0x22316e8, 0xc00100b680, 0xc000563300)
    .../server/http/handlers/middleware/inject.go:57 +0x83
net/http.HandlerFunc.ServeHTTP(0xc00100e780, 0x22316e8, 0xc00100b680, 0xc000563300)
    /usr/local/go/src/net/http/server.go:2069 +0x44
.../server/http/handlers/middleware.SetAmqpMessager.func1.1(0x22316e8, 0xc00100b680, 0xc000563200)
    .../server/http/handlers/middleware/inject.go:48 +0xab
net/http.HandlerFunc.ServeHTTP(0xc00100e7b0, 0x22316e8, 0xc00100b680, 0xc000563200)
    /usr/local/go/src/net/http/server.go:2069 +0x44
.../server/http/handlers/middleware.SetGrpcService.func1.1(0x22316e8, 0xc00100b680, 0xc000563100)
    .../server/http/handlers/middleware/inject.go:39 +0xa2
net/http.HandlerFunc.ServeHTTP(0xc000cd6ec0, 0x22316e8, 0xc00100b680, 0xc000563100)
    /usr/local/go/src/net/http/server.go:2069 +0x44
.../server/http/handlers/middleware.SetRegistryClient.func1(0x22316e8, 0xc00100b680, 0xc000563000)
    .../server/http/handlers/middleware/inject.go:31 +0x24d
net/http.HandlerFunc.ServeHTTP(0xc000cc5d10, 0x22316e8, 0xc00100b680, 0xc000563000)
    /usr/local/go/src/net/http/server.go:2069 +0x44
.../server/http/handlers/middleware.SetDBHandler.func1.1(0x22316e8, 0xc00100b680, 0xc000562f00)
    .../server/http/handlers/middleware/inject.go:22 +0xab
net/http.HandlerFunc.ServeHTTP(0xc00100e7e0, 0x22316e8, 0xc00100b680, 0xc000562f00)
    /usr/local/go/src/net/http/server.go:2069 +0x44
github.com/gorilla/handlers.recoveryHandler.ServeHTTP(0x2210e60, 0xc00100e7e0, 0x0, 0x0, 0x1, 0x22316e8, 0xc00100b680, 0xc000562f00)
    /app/vendor/github.com/gorilla/handlers/recovery.go:78 +0xce
.../server/http/handlers/middleware.RequestID.func1(0x22316e8, 0xc00100b680, 0xc000562e00)
    .../server/http/handlers/middleware/request_id.go:19 +0x270
net/http.HandlerFunc.ServeHTTP(0xc000cc5d28, 0x22316e8, 0xc00100b680, 0xc000562e00)
    /usr/local/go/src/net/http/server.go:2069 +0x44
github.com/gorilla/mux.(*Router).ServeHTTP(0xc0001db200, 0x22316e8, 0xc00100b680, 0xc000562c00)
    /app/vendor/github.com/gorilla/mux/mux.go:210 +0xd3
github.com/felixge/httpsnoop.CaptureMetrics.func1(0x22316e8, 0xc00100b680)
    /app/vendor/github.com/felixge/httpsnoop/capture_metrics.go:29 +0x4c
github.com/felixge/httpsnoop.CaptureMetricsFn(0x2230398, 0xc000cce1c0, 0xc000dbd848, 0xc000da7880, 0x203000, 0x7fba9ad1c190)
    /app/vendor/github.com/felixge/httpsnoop/capture_metrics.go:76 +0x20a
github.com/felixge/httpsnoop.CaptureMetrics(0x220ab20, 0xc0001db200, 0x2230398, 0xc000cce1c0, 0xc000562c00, 0x7fba9ac341d0, 0x800, 0x5)
    /app/vendor/github.com/felixge/httpsnoop/capture_metrics.go:28 +0x7d
e.coding.net/codingcorp/coding-artifacts/internal/server/docker_core/server/http/handlers/middleware.Logging.func1(0x2230398, 0xc000cce1c0, 0xc000562c00)
    /app/internal/server/docker_core/server/http/handlers/middleware/logging.go:15 +0x6f
net/http.HandlerFunc.ServeHTTP(0xc000ece960, 0x2230398, 0xc000cce1c0, 0xc000562c00)
    /usr/local/go/src/net/http/server.go:2069 +0x44
net/http.serverHandler.ServeHTTP(0xc0006582a0, 0x2230398, 0xc000cce1c0, 0xc000562c00)
    /usr/local/go/src/net/http/server.go:2887 +0xa3
net/http.(*conn).serve(0xc000dac780, 0x2239e70, 0xc000cdbd00)
    /usr/local/go/src/net/http/server.go:1952 +0x8cd
created by net/http.(*Server).Serve
    /usr/local/go/src/net/http/server.go:3013 +0x39b

中间件:

func (app *App) useMiddlewares() 
    app.router.Use(middleware.RequestID)
    app.router.Use(handlers.RecoveryHandler(handlers.PrintRecoveryStack(true)))
    app.router.Use(
        middleware.SetDBHandler(app.db),
        middleware.SetRegistryClient,
        middleware.SetGrpcService(app.grpcService),
    )

    app.router.Use(middleware.SetAmqpMessager(messager))
    app.router.Use(middleware.SetMigrater(migrater))

更多信息:

新路由器:

// RouterWithPrefix builds a gorilla router with a configured prefix
// on all routes.
func RouterWithPrefix(prefix string) *mux.Router 
    rootRouter := mux.NewRouter()
    router := rootRouter
    if prefix != "" 
        router = router.PathPrefix(prefix).Subrouter()
    

    router.StrictSlash(true)

    for _, descriptor := range routeDescriptors 
        router.Path(descriptor.Path).Name(descriptor.Name)
    

    return rootRouter

服务这个 http 服务器:

func (app *App) ListenAndServe(port int) error 
    svc := &servicegrpcService: app.grpcService

    // base
    r := app.router.PathPrefix("/v2").Subrouter()
    r.Use(
        middleware.Whitelist,
        middleware.PassInternalRequest,
        middleware.Authenticate(app.grpcService),
        middleware.SetArtInfo(app.grpcService),
    )

    // /v2/
    r.HandleFunc("/", proxyutil.ServeHTTP).Methods(http.MethodGet)

    // other handlers ...

    loggedHandler := middleware.Logging(app.router)
    app.server = &http.Server
        Addr:    httputil.Addr(port),
        Handler: loggedHandler,
    

    log.Info("Serving the HTTP server successfully", zap.Int("port", port))
    if err := app.server.ListenAndServe(); err != nil && !errors.IgnoreError(err) 
        return err
    

    return nil

测试:

curl -v http://localhost:8080/v2/
500 Internal Server Error

我的 go 版本:go version go1.16.5 linux/amd64

gorilla/mux 版本:github.com/gorilla/mux v1.8.0

我真的很困惑为什么下一个处理程序是 nil ..

因为它是nil,我的路由器的下一个处理程序将永远不会被执行,包括我真正的处理程序,甚至不是中间件。

如果我有什么问题?我想不通。

【问题讨论】:

请勿发布文字图片。 请以文字形式分享minimal reproducible example。 @JuanChan 你能在60 行显示文件../middleware/inject.go 中的内容吗? @Volker 我已将图像更改为文本 @mkopriva 我已经更新了这篇文章,并指出了恐慌发生的地方。你会看到next.ServeHTTP(w, r) // next is nil ! panic here 【参考方案1】:

谢谢大家,我已经解决了这个问题。

我使用alice 来包装中间件和我的处理程序:

func (app *App) addMiddlewares() http.Handler 
    return alice.New(
        middleware.Logging,
        middleware.RequestID,
        // some other middlewares ...
        middleware.SetMigrater(migrater),
    ).Then(app.router)

func (app *App) ListenAndServe(port int) error 
    svc := &service

    // base
    r := app.router.PathPrefix("/v2").Subrouter()
    r.Use(
        middleware.Whitelist,
        middleware.PassInternalRequest,
        middleware.Authenticate(app.grpcService),
        middleware.SetArtInfo(app.grpcService),
    )

    // /v2/
    r.HandleFunc("/", proxyutil.ServeHTTP).Methods(http.MethodGet)
    // wrap the handler
    handler := app.addMiddlewares()
    app.server = &http.Server
        Addr:    httputil.Addr(port),
        Handler: handler,
    

    log.Info("Serving the HTTP server successfully", zap.Int("port", port))
    if err := app.server.ListenAndServe(); err != nil && !errors.IgnoreError(err) 
        return err
    

    return nil

我所做的一件重要的事情是将新多路复用路由器的方式从router := RouterWithPrefix("") 更改为router := mux.NewRouter()

这可以避免我的处理程序出现 404。

那么,不再有NPE,和平与爱。

再次感谢大家:)

【讨论】:

以上是关于中间件中的 gorilla/mux 下一个处理程序是 nil的主要内容,如果未能解决你的问题,请参考以下文章

Gorilla mux 自定义中间件

使用 Gorilla MUX 和 Negroni 子路由中间件

Gorilla mux,静态文件服务器的中间件

Gorilla Mux 处理 curl 请求

如何在 gorilla/mux 包中初始化 HandleFunc 中的变量

Go 每日一库之 gorilla/handlers