我希望从我的处理程序中提取一些重复逻辑并将其放入一些每个处理程序中间件:特别是像CSRF检查,检查现有会话值(即用于身份验证或用于预览页面)等.
我已经阅读了一些关于此的文章,但是很多例子都集中在每个服务器的中间件(包装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/
URL,否则它会将它们重定向(因为它们没有任何内容可供预览!).
我想将该结构传递给包装的处理程序以节省必须复制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更加棘手/更复杂.
将大猩猩/背景更适合这里,让我避免写2-3自定义的处理器类型(或通用处理器类型) -如果有的话,我会如何利用它?或者我可以实现自己的"上下文"映射(并遇到并发访问问题?).
在可能的情况下,我试图避免陷入"不要被抓到写一个库"陷阱,但中间件是我可能会在项目生命的后期添加/构建的东西,我想"把它弄好"第一次来.
对此的一些指导将非常感激.到目前为止,围绕编写Web应用程序一直很好,但是在它的生命的这个阶段并没有大量的例子,因此我倾向于一点点.
如果我正确地理解了您的问题,那么您正在寻找一种方便的方法将其他参数传递给您的中间件,对吧?
现在,定义这些参数是很重要的.它们可能是中间件的一些配置值 - 可以在构造Handler类型时设置它们.而不是NewMyMiddleware(MyHandler)
,你NewMyMiddleware(MyHandler, "parameter")
没有问题.
但在您的情况下,您似乎想要传递每个请求参数,例如CSRF令牌.将它们传递给处理函数会修改它的签名,它会偏离标准Handler[Func]
接口.在这种情况下,你对中间件更紧密耦合是正确的.
您自己提到了解决方案 - 在我看来,上下文地图是一个可行的工具.这不是说很难自己写一个-你基本上需要map[*http.Request]interface{}
和RWMutex
安全的并发访问.仍然,只需使用gorilla/context
就足够了 - 它似乎是一个(相对)成熟,写得很好的包,有一个很好的API.
无耻的插件:如果您正在处理CSRF检查,为什么不尝试我的nosurf包?