如何将数据从中间件传递到处理程序?

Posted

技术标签:

【中文标题】如何将数据从中间件传递到处理程序?【英文标题】:How can I pass data from middleware to handlers? 【发布时间】:2015-07-19 18:22:52 【问题描述】:

我正在设计我的处理程序以返回 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 一起使用。

【问题讨论】:

【参考方案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 访问它。

【讨论】:

是的,这很好用。您可以在此处找到更多示例和说明:gocodecloud.com/blog/2016/11/15/…【参考方案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 结构中,您可以放置​​您想要传递的任何数据。

【讨论】:

以上是关于如何将数据从中间件传递到处理程序?的主要内容,如果未能解决你的问题,请参考以下文章

将数据传递到更下游的中间件

如何将数据从顶点着色器传递到片段着色器,中间有着色器[重复]

如何将数据从 express 中间件直接传递回客户端 JWT 和 Express 中间件

Node、Express、Mongoose 如何将 doc._id 从函数传递到 next() 中间件

Laravel - 将变量从中间件传递到控制器/路由

如何将自定义标头从 mvc 项目发送到 Web api 项目?