我希望从我的处理程序中提取一些重复的逻辑并将其放入一些每个处理程序的中间件中:特别是 CSRF 检查、检查现有 session 值(即用于身份验证或用于预览页面)等.
我读过 a few articles on this ,但许多示例都侧重于每服务器中间件(包装 http.Handler
):我有一小部分需要中间件的处理程序。我的大多数其他页面都没有,因此如果我可以避免检查 session /等。对于那些要求更好。
到目前为止,我的中间件通常看起来像这样:
func checkCSRF(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// get the session, check/validate/create the token based on HTTP method, etc.
// return HTTP 403 on a failed check
// else invoke the wrapped handler h(w, r)
}
}
然而,在很多情况下,我想将一个变量传递给包装处理程序:一个生成的 CSRF token 传递给模板,或者一个包含表单数据的结构——一个中间件检查 session 中是否存在一些保存的在用户点击 /preview/
URL 之前形成数据,否则它会将它们重定向(因为它们没有可预览的内容!)。
我想将该结构传递给包装的处理程序,以避免重复 session 。我刚刚在中间件中编写的获取/类型断言/错误检查逻辑。
我可以这样写:
type CSRFHandlerFunc func(w http.ResponseWriter, r *http.Request, t string)
...然后像这样编写中间件:
func csrfCheck(h CSRFHandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// get the session, check/validate/create the/a token based on HTTP method, etc.
// return HTTP 403 on a failed check
// else invoke the wrapped handler and pass the token h(w, r, token)
}
...但这提出了几个问题:
- 这是实现每个处理程序中间件和传递每个请求变量的明智方法吗?
- 在测试这个之前(没有访问我的开发机器!),如果我需要用多个中间件包装一个处理程序,我假设我可以
r.HandleFunc("/path/preview/", checkCSRF(checkExisting(previewHandler)))
?我在这里看到的问题是中间件现在紧密耦合:包装的中间件现在需要接收然后传递来自外部中间件的变量。这使得扩展 http.HandlerFunc 更棘手/更复杂。 - 会 gorilla/context 在这里更合适并且允许我避免编写 2-3 个自定义处理程序类型(或通用处理程序类型)——如果是这样,我将如何使用它?或者我可以实现自己的“上下文”映射(并遇到并发访问问题吗?)。
在可能的情况下,我试图避免落入“不要被抓到写库”的陷阱,但中间件是我可能会在项目生命周期的后期添加/构建的东西,而且我会喜欢第一次就“做对”。
对此提供一些指导将不胜感激。到目前为止,Go 在编写 Web 应用程序方面很棒,但在其生命的这个阶段并没有大量示例,因此我有点依赖 SO。
最佳答案
如果我对您的问题的理解正确,您正在寻找一种方便的方法来将额外的参数传递给您的中间件,对吧?
现在,定义这些参数是什么很重要。它们可能是您的中间件的一些配置值——这些可以在构造 Handler 类型时设置)。您可以使用 NewMyMiddleware(MyHandler, "parameter")
而不是 NewMyMiddleware(MyHandler)
,这没问题。
但在您的情况下,您似乎想要传递每个请求的参数,例如 CSRF token 。将它们传递给处理程序函数会修改其签名,并且会偏离标准的 Handler[Func]
接口(interface)。在这种情况下,您认为中间件耦合更紧密是正确的。
您自己提到了解决方案——上下文映射是,在我看来,这是一个可行的工具。自己编写一个并不难 – 您基本上需要一个 map[*http.Request]interface{}
和一个 RWMutex
来实现安全并发使用权。尽管如此,简单地使用 gorilla/context
应该就足够了——它看起来像是一个(相对)成熟、编写良好的包,带有一个很好的 API。
无耻插件:如果你正在处理 CSRF 检查,为什么不试试我的 nosurf包?
关于http - 编写每个处理程序的中间件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19784687/