Go 请求完成后,Negroni继续调用其他处理程序
我在Go中的web应用程序(使用GorillaGo 请求完成后,Negroni继续调用其他处理程序,go,gorilla,negroni,Go,Gorilla,Negroni,我在Go中的web应用程序(使用Gorillamux和negroni)有大约20个处理程序,根据应该应用的中间件功能分为三组。具体而言: 组1:静态请求(根本没有中间件) 组2:仅应具有CORS中间件的请求,无身份验证: GET / GET /login POST /login GET /auth-configuration GET /service-status 组3:应同时应用CORS和身份验证中间件的请求: GET /articles POST /artic
mux
和negroni
)有大约20个处理程序,根据应该应用的中间件功能分为三组。具体而言:
- 组1:静态请求(根本没有中间件)
- 组2:仅应具有CORS中间件的请求,无身份验证:
GET / GET /login POST /login GET /auth-configuration GET /service-status
- 组3:应同时应用CORS和身份验证中间件的请求:
GET /articles POST /articles PUT /articles/etc PATCH /articles/etc
func run() {
negroniStack := setUpNegroni()
bindAddr := // ...
http.ListenAndServe(bindAddr, negroniStack)
}
func setUpNegroni() negroni.Negroni {
negroniStack := negroni.Negroni{}
staticNegroni := setUpRoutesAndMiddlewareForStaticRequests()
loginNegroni := setUpRoutesAndMiddlewareForLogin()
serviceNegroni = setUpRoutesAndMiddlewareForService()
negroniStack.UseHandler(&staticNegroni)
negroniStack.UseHandler(&loginNegroni)
negroniStack.UseHandler(&serviceNegroni)
return negroniStack
}
func setUpRoutesAndMiddlewareForStaticRequests() negroni.Negroni {
staticNegroni := negroni.Negroni{}
staticRouter := mux.NewRouter()
staticRouter.PathPrefix("/files").HandlerFunc(staticHandler)
staticRouter.Path("/favicon.ico").HandlerFunc(staticHandler)
staticNegroni.UseHandler(staticRouter)
return staticNegroni
}
func setUpRoutesAndMiddlewareForLogin() negroni.Negroni {
authNegroni := negroni.Negroni{}
corsMiddleware := cors.New(cors.Options{
AllowedMethods: []string{"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"},
AllowCredentials: true,
OptionsPassthrough: false,
})
authNegroni.Use(corsMiddleware)
authRouter := mux.NewRouter()
authRouter.HandleFunc("/login", HandlePostAuth).Methods("POST")
authRouter.HandleFunc("/login", HandleGetAuth) // GET
authNegroni.UseHandler(authRouter)
return authNegroni
}
func setUpRoutesAndMiddlewareForService() negroni.Negroni {
serviceNegroni := negroni.Negroni{}
corsMiddleware := cors.New(cors.Options{
AllowedMethods: []string{"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"},
AllowCredentials: true,
OptionsPassthrough: false,
})
serviceNegroni.Use(corsMiddleware)
serviceNegroni.UseFunc(jwtMiddleware)
serviceRouter := mux.NewRouter()
serviceRouter.HandleFunc("/articles", HandleGetArticles).Methods("GET")
serviceRouter.HandleFunc("/articles", HandlePostArticles).Methods("POST")
// etc
serviceNegroni.UseHandler(serviceRouter)
return serviceNegroni
}
基于“路由特定中间件”一节,我认为这是正确的,其中指出:
如果您有一组需要执行特定中间件的路由,只需创建一个新的Negroni实例并将其用作路由处理程序
但是,当我发出请求并使用调试器时,我看到(*Negroni).ServeHTTP
被多次调用。例如,如果我请求GET/favicon.ico
,则正确调用staticHandler
函数并调用WriteHeader(200)
,但之后它调用下一个mux.Router
,后者调用WriteHeader(404)
在终端中打印警告,因为头被写入了两次(http:multiple response.WriteHeader calls
)
如果它用于不存在的路由,那么Gorilla默认的NotFoundHandler
将被调用3次(每个mux.Router
调用一次)
如何让Negroni在请求完成后停止调用其他处理程序
…如果我错误配置了Negroni实例,为什么它不在初始化期间执行检查以警告我配置无效
我的理解是
negroni.Use
和UseFunc
用于设置中间件(每个请求都会调用它们),而UseHandler
用于设置终端处理程序(每个请求只调用1个,或者回退到404)。如果我正确理解了这种情况,那么出于某种原因,它将我的终端处理程序视为中间件。来自UseHandler
文档()
UseHandler将http.Handler添加到中间件堆栈中按照处理程序添加到Negroni的顺序调用处理程序。
因此,你在这里看到的似乎是预期的行为
您基本上是在创建不同的negroni实例并将它们链接起来,因此最终的Negroistack
本身就是一个中间件,它将执行您添加的其他中间件
我相信您要做的是使用实际路由器创建路由,然后向每个路由添加适当的中间件(使用negroni)
如果您查看从文档链接的示例,这就是他们在该部分()中所做的
请注意,它们不是嵌套negroni实例,而是只创建一个应用于所需路由的实例。这里是路由的完整列表吗?你有像“/{category}”这样的路由吗?@vitr我可以用这里发布的代码重现这个问题。但是我没有使用
PathPrefix
或Subrouter
,所以我必须对每个路由重复negroni.New(mw1,mw2,negroni.Wrap(HandlerNameHere))
。我不喜欢那种设计——它不太干。有没有更好的方法?@Dai:你肯定必须重新构造你的构建方式,不重复代码的一个选项是创建一个函数来创建negroni,并包装你正在创建的任何处理程序,并像wrapWithCors(myHandler)一样调用它。。。只是一个例子是的,这会起作用——尽管感觉还是不对。对我来说,将路由器(及其所有已注册的路由)封装在中间件层比将其与每个路由相关联更有意义,但我看不出是如何实现的,因为除非它们位于层次结构中,否则无法“合并”router
对象。这最终起到了作用——尽管没有调用negroni.New(mw1,mw2,negroni.Wrap(ActualHandler))
在router.Handler()方法中,我使用了另一个函数,但原理相同。
func run() {
negroniStack := setUpNegroni()
bindAddr := // ...
http.ListenAndServe(bindAddr, negroniStack)
}
func setUpNegroni() negroni.Negroni {
negroniStack := negroni.Negroni{}
staticNegroni := setUpRoutesAndMiddlewareForStaticRequests()
loginNegroni := setUpRoutesAndMiddlewareForLogin()
serviceNegroni = setUpRoutesAndMiddlewareForService()
negroniStack.UseHandler(&staticNegroni)
negroniStack.UseHandler(&loginNegroni)
negroniStack.UseHandler(&serviceNegroni)
return negroniStack
}
func setUpRoutesAndMiddlewareForStaticRequests() negroni.Negroni {
staticNegroni := negroni.Negroni{}
staticRouter := mux.NewRouter()
staticRouter.PathPrefix("/files").HandlerFunc(staticHandler)
staticRouter.Path("/favicon.ico").HandlerFunc(staticHandler)
staticNegroni.UseHandler(staticRouter)
return staticNegroni
}
func setUpRoutesAndMiddlewareForLogin() negroni.Negroni {
authNegroni := negroni.Negroni{}
corsMiddleware := cors.New(cors.Options{
AllowedMethods: []string{"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"},
AllowCredentials: true,
OptionsPassthrough: false,
})
authNegroni.Use(corsMiddleware)
authRouter := mux.NewRouter()
authRouter.HandleFunc("/login", HandlePostAuth).Methods("POST")
authRouter.HandleFunc("/login", HandleGetAuth) // GET
authNegroni.UseHandler(authRouter)
return authNegroni
}
func setUpRoutesAndMiddlewareForService() negroni.Negroni {
serviceNegroni := negroni.Negroni{}
corsMiddleware := cors.New(cors.Options{
AllowedMethods: []string{"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"},
AllowCredentials: true,
OptionsPassthrough: false,
})
serviceNegroni.Use(corsMiddleware)
serviceNegroni.UseFunc(jwtMiddleware)
serviceRouter := mux.NewRouter()
serviceRouter.HandleFunc("/articles", HandleGetArticles).Methods("GET")
serviceRouter.HandleFunc("/articles", HandlePostArticles).Methods("POST")
// etc
serviceNegroni.UseHandler(serviceRouter)
return serviceNegroni
}
router.PathPrefix("/admin").Handler(negroni.New(
Middleware1,
Middleware2,
negroni.Wrap(adminRoutes),
))