本文介绍了编写每个Handler中间件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
限时删除!!
我希望从我的处理程序中提取一些重复的逻辑,并将其放到每个处理程序中间件中:特别是像CSRF检查,检查现有会话值(例如,auth或预览页面)等等。
我读过,但许多例子都关注每个服务器中间件(包装 http.Handler
):我有一组较小的处理程序需要中间件。我的大多数其他页面都没有,因此如果我能避免检查会话/等。对于那些请求更好。
到目前为止,我的中间件通常看起来像这样:
func checkCSRF(h http.HandlerFunc)http.HandlerFunc {
return func(w http.ResponseWriter,r * http.Request){
//获取会话,检查/根据HTTP方法验证/创建令牌
//在失败的检查
上返回HTTP 403 //否则调用包装的处理程序h(w,r)
} $ b $然而,在很多情况下,我想将一个变量传递给包装处理程序:一个生成的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){
//获取会话,根据HTTP方法检查/验证/创建/ a标记等
//返回HTTP 403失败检查
// else调用包装的处理程序并传递令牌h(w,r,令牌)
}
...但是引发了一些问题:
- 这是一个合理的方法实现每个处理程序中间件并传递每个请求变量?
- 在测试之前(不能访问我的开发机器!),如果我需要用多个中间件,我作为sume我可以只是
r.HandleFunc(/ path / preview /,checkCSRF(checkExisting(previewHandler)))
?我在这里看到的问题是中间件现在紧密耦合在一起:包裹的中间件现在需要接收并从外部中间件传递变量。这使得扩展http.HandlerFunc变得更复杂/更复杂。 在这里更合适,允许我避免编写2-3个自定义处理程序类型(或通用处理程序类型) - 如果是,我将如何使用它?或者我可以实现自己的上下文映射(并遇到并发访问问题?)。
在可能的情况下,我正在尝试避免因为不要被抓到写一个图书馆陷阱而陷入困境,但是中间件是我可能在项目的后期增加/构建的东西,而且我希望在第一个时候做得对时间。
有关这方面的一些指导将非常感谢。到目前为止,编写一个Web应用程序的时间已经很多了,但是在这个阶段并没有太多的例子,所以我有点偏向于这个。
$ b $如果我正确地理解了你的问题,你正在寻找一种方便的方法来传递额外的参数到你的中间件,对吧?
$ div class =h2_lin>解决方案
现在,定义这些参数是很重要的。它们可能是中间件的一些配置值 - 可以在构建Handler类型时设置它们)。而不是 NewMyMiddleware(MyHandler)
,你可以做 NewMyMiddleware(MyHandler,parameter)
,这里没有问题。 p>
但在你的情况下,你似乎想传递每个请求参数,比如CSRF令牌。将它们传递到处理函数会修改它的签名,它会偏离标准 Handler [Func]
界面。你说得对,中间件在这种情况下更加紧密。
我的看法,这是一个可行的工具。不是
很难自己写一个 - 你基本上需要一个
map [* http.Request]接口{}
和一个
RWMutex
以实现安全的并发访问。尽管如此,简单地使用
gorilla / context
就足够了 - 它看起来像一个(相对)成熟的,编写良好的API,具有很好的API。
无耻的插件:如果你正在处理CSRF检查,为什么不试试我的 nosurf 包?
I'm looking to pull some repetitive logic out of my handlers and put it into some per-handler middleware: specifically things like CSRF checks, checking for an existing session value (i.e. for auth, or for preview pages), etc.
I've read a few articles on this, but many examples focus on a per-server middleware (wrapping http.Handler
): I have a smaller set of handlers that need the middleware. Most of my other pages do not, and therefore if I can avoid checking sessions/etc. for those requests the better.
My middleware, so far, typically looks something like this:
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)
}
}
However, in many cases I want to pass a variable to the wrapped handler: a generated CSRF token to pass to the template, or a struct that contains form data—one piece of middleware checks the session for the presence of some saved form data before the user hits a /preview/
URL, else it redirects them away (since they have nothing to preview!).
I'd like to pass that struct along to the wrapped handler to save having to duplicate the session.Get/type assertion/error checking logic I just wrote in the middleware.
I could write something like:
type CSRFHandlerFunc func(w http.ResponseWriter, r *http.Request, t string)
... and then write the middleware like so:
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)
}
... but that raises a few questions:
- Is this a sensible way to implement per-handler middleware and pass per-request variables?
- Prior to testing this (don't have access to my dev machine!), if I need to wrap a handler with multiple pieces of middleware, I assume I can just
r.HandleFunc("/path/preview/", checkCSRF(checkExisting(previewHandler)))
? The issue I'm seeing here is that the middleware is now tightly coupled: the wrapped middleware now needs to receive and then pass on the variable from the outer middleware. This makes extending http.HandlerFunc trickier/more convoluted. - Would gorilla/context fit better here and allow me to avoid writing 2-3 custom handler types (or a generic handler type) — and if so, how would I make use of it? Or could I implement my own "context" map (and run into issues with concurrent access?).
Where possible I'm trying to avoid falling for the "don't get caught writing a library" trap, but middleware is something that I'm likely to add/build on later in the project's life, and I'd like to "get it right" the first time around.
Some guidance on this would be much appreciated. Go's been great so far for writing a web application, but there's not a ton of examples around at this stage in its life and I'm therefore leaning on SO a little.
解决方案
If I understood your question correctly, you're looking for a convenient way to pass additional parameters to your middleware, right?
Now, it's important to define what those parameters are. They could be some configuration values for your middleware – those can be set when the Handler type is being constructed). Instead of NewMyMiddleware(MyHandler)
, you do NewMyMiddleware(MyHandler, "parameter")
, no problem here.
But in your case it seems like you want to pass per-request parameters, like a CSRF token. Passing those into the handler function would modify its signature and it would deviate from the standard Handler[Func]
interface. You're right about middleware being more tightly coupled in this case.
You kind of mentioned the solution yourself – a context map is, in my opinion, a viable tool for this. It's not that hard to write one yourself – you basically need a map[*http.Request]interface{}
and an RWMutex
for safe concurrent access. Still, simply using gorilla/context
should suffice – it seems like a (relatively) mature, well-written package with a nice API.
Shameless plug: if you're dealing with CSRF checks, why not try out my nosurf package?
这篇关于编写每个Handler中间件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
1403页,肝出来的..