【问题标题】:How can I pass data from middleware to handlers?如何将数据从中间件传递到处理程序?
【发布时间】:2015-10-08 20:45:33
【问题描述】:

我正在设计我的处理程序以返回 http.Handler。这是我的处理程序的设计:

 func Handler() http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  })
}

我的中间件设计为接受 http.Handler,然后在中间件完成操作后调用处理程序。这是我的中间件的设计:

 func Middleware(next http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    // Middleware operations

    next.ServeHTTP(w, r)
  })
}

考虑到我的中间件和处理程序的设计,将信息从中间件传递到处理程序的正确方法是什么?我试图从中间件传递给处理程序的信息是从请求正文解析的 JSON Web 令牌。如果我没有将解析的 JWT 传递给处理程序,那么我将需要在我的处理程序中再次解析 JWT。在中间件和处理程序中解析 JWT 的请求正文似乎很浪费。以防万一这些信息是相关的,我将标准的 net/http 库与 gorilla mux 一起使用。

【问题讨论】:

    标签: go


    【解决方案1】:

    既然您已经在使用Gorilla,请查看context 包。

    (如果您不想更改方法签名,这很好。)

    import (
        "github.com/gorilla/context"
    )
    
    ...
    
    func Middleware(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            // Middleware operations
            // Parse body/get token.
            context.Set(r, "token", token)
    
            next.ServeHTTP(w, r)
        })
    }
    
    ...
    
    func Handler() http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            token := context.Get(r, "token")
        })
    }
    

    更新

    Gorilla context 包现在处于维护模式
    根据 repo:

    注意 gorilla/context 在 context.Context 存在之前就已经诞生了,它不能很好地与 http.Request.WithContext(添加到 net/http Go 1.7 及更高版本)执行的请求的浅拷贝。

    在这些情况下使用 gorilla/context 可能会导致内存泄漏,因为指向每个 http.Request 的指针都变得“孤立”并且在发送响应时不会被清理。

    您应该使用 Go 1.7 中的 http.Request.Context() 功能。

    【讨论】:

    • 与直接使用内置 golang 上下文相比有什么好处?
    • @KarelBílek Poster 提到使用 Gorilla 工具包,它有自己的上下文包,早于 Go (与许多其他库一样)。它现在处于维护模式,他们建议使用 Go 的 context 包。更新了这个答案以反映,谢谢。
    • 啊,我错过了约会。好的
    【解决方案2】:

    现在传递请求范围数据的正确方法是标准库中的上下文包。

    https://golang.org/pkg/context/

    您可以通过 http.Request 上的 request.Context 访问它。

    【讨论】:

    【解决方案3】:

    类似于问题的第一种方法是codemodus/chain by Daved

    包链有助于组成承载请求范围数据的处理程序包装链。

    它使用notion of Context,加上一个上下文处理程序:

    func ctxHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
        // ...
    
        if s, ok := getMyString(ctx); ok {
            // s = "Send this down the line."
        }
    
        // ...
    }
    

    另一种方法:您可以查看Matt Silverlock (elithrar) 的“Custom Handlers and Avoiding Globals in Go Web Applications”。 (full example here)

    这个想法是在包含相关上下文的类型上定义ServeHTTP

    // We've turned our original appHandler into a struct with two fields:
    // - A function type similar to our original handler type (but that now takes an *appContext)
    // - An embedded field of type *appContext
    type appHandler struct {
        *appContext
        h func(*appContext, http.ResponseWriter, *http.Request) (int, error)
    }
    
    // Our ServeHTTP method is mostly the same, and also has the ability to
    // access our *appContext's fields (templates, loggers, etc.) as well.
    func (ah appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        // Updated to pass ah.appContext as a parameter to our handler type.
        status, err := ah.h(ah.appContext, w, r)
        if err != nil {
            log.Printf("HTTP %d: %q", status, err)
            switch status {
            case http.StatusNotFound:
                http.NotFound(w, r)
                // And if we wanted a friendlier error page, we can
                // now leverage our context instance - e.g.
                // err := ah.renderTemplate(w, "http_404.tmpl", nil)
            case http.StatusInternalServerError:
                http.Error(w, http.StatusText(status), status)
            default:
                http.Error(w, http.StatusText(status), status)
            }
        }
    }
    

    appContext 结构中,您可以放置​​您想要传递的任何数据。

    【讨论】:

      猜你喜欢
      • 2018-02-21
      • 2023-02-17
      • 2020-01-06
      • 1970-01-01
      • 2022-10-05
      • 1970-01-01
      • 2019-09-18
      • 1970-01-01
      相关资源
      最近更新 更多