编写Per-Handler中间件

时间:2013-11-05 08:36:11

标签: http go middleware

我希望从我的处理程序中提取一些重复逻辑并将其放入一些每个处理程序中间件中:特别是像CSRF检查,检查现有会话值(即用于auth或用于预览页面)等等

我已阅读a few articles on this,但很多示例都关注每服务器中间件(包装http.Handler):我有一小部分需要中间件的处理程序。我的大多数其他页面都没有,因此如果我可以避免检查会话/等。对于那些要求更好。

到目前为止,我的中间件通常看起来像这样:

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令牌传递给模板,或者包含表单数据的结构 - 一个中间件检查会话是否存在某些已保存的在用户点击/preview/网址之前填写表单数据,否则会将其重定向(因为他们无需预览!)。

我想将该结构传递给包装处理程序,以节省必须复制session.Get / type断言/错误检查逻辑我刚刚在中间件中写道。

我可以这样写:

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应用程序,但是在其生命的这个阶段并没有大量的例子,因此我倾向于那么一点。

1 个答案:

答案 0 :(得分:6)

如果我理解你的问题,你正在寻找一种方便的方法将其他参数传递给你的中间件,对吗?

现在,定义这些参数是很重要的。它们可能是中间件的一些配置值 - 可以在构造Handler类型时设置它们。你做NewMyMiddleware(MyHandler)而不是NewMyMiddleware(MyHandler, "parameter"),而不是问题。

但在您的情况下,您似乎希望传递每个请求参数,例如CSRF令牌。将它们传递给处理函数将修改其签名,它将偏离标准Handler[Func]接口。在这种情况下,你对中间件更紧密耦合是正确的。

您自己提到了解决方案 - 在我看来,背景图 是一个可行的工具。您自己很难写 - 您基本上需要map[*http.Request]interface{}RWMutex才能安全地进行并发访问。尽管如此,简单地使用gorilla/context就足够了 - 它似乎是一个(相对)成熟,编写良好的包,并且有一个很好的API。

无耻插件:如果您正在处理CSRF检查,为什么不试用我的nosurf包?