我希望从我的处理程序中提取一些重复的逻辑并将其放入一些每个处理程序的中间件中:特别是 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 更棘手/更复杂。 - gorilla /context是否更适合这里并允许我避免编写 2-3 个自定义处理程序类型(或通用处理程序类型)——如果是这样,我将如何使用它?或者我可以实现自己的“上下文”映射(并遇到并发访问问题?)。
在可能的情况下,我试图避免陷入“不要陷入编写库”的陷阱,但中间件是我可能会在项目生命的后期添加/构建的东西,我想“做对了”第一次。
对此的一些指导将不胜感激。到目前为止,Go非常适合编写 Web 应用程序,但在其生命的这个阶段并没有大量的示例,因此我稍微倾向于 SO。