Negroni 在请求完成后继续调用其他处理程序
Negroni continues to call other handlers after request is completed
我在 Go 中的 web-application(使用 Gorilla mux
和 negroni
)有大约 20 个处理程序,根据应应用的中间件功能分为三组。具体来说:
第 1 组:静态请求(根本没有中间件)
GET /favicon.ico
GET /files
GET /files/index.html
GET /files/favicon.ico
第 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
这是我的代码,sets-up HTTP 服务器:
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's documentation 中的 "Route Specific Middleware" 部分,我认为这是正确的,其中写着:
If you have a route group of routes that need specific middleware to be executed, you can simply create a new Negroni instance and use it as your route handler.
但是,当我发出请求并使用调试器时,我发现 (*Negroni).ServeHTTP
被多次调用。例如,如果我请求 GET /favicon.ico
那么 staticHandler
函数被正确调用并调用 WriteHeader(200)
,但之后它会调用下一个 mux.Router
调用 WriteHeader(404)
因为 header 被写入了两次 (http: multiple response.WriteHeader calls
)
,所以在终端中打印出一条警告
如果它用于不存在的路由,则 Gorilla 默认值 NotFoundHandler
被调用 3 次(每个 mux.Router
一次)。
如何让 Negroni 在请求完成后停止调用其他处理程序?
...如果我错误配置了我的 Negroni 实例,为什么它不在初始化期间执行检查以警告我配置无效?
我的理解是 negroni.Use
和 UseFunc
是针对 setting-up 中间件(每个请求都会调用),而 UseHandler
是针对 set-up 终端处理程序(每个请求只调用 1 个,或回退到 404)。如果我正确理解了这种情况,那么出于某种原因它将我的终端处理程序视为中间件。
来自 UseHandler
文档 (https://godoc.org/github.com/urfave/negroni#Negroni.UseHandler)
UseHandler adds a http.Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni.
所以您在这里看到的似乎是预期的行为。
您基本上是在创建不同的 negroni 实例并将它们链接起来,因此您的最终 negroniStack
本身就是一个中间件,它将执行您添加的其他中间件。
我相信您想做的是使用实际路由器创建路由,然后将适当的中间件(使用 negroni)添加到每个路由。
如果您查看从文档链接的示例,这就是他们在该部分所做的事情 (https://github.com/urfave/negroni#route-specific-middleware)。
router.PathPrefix("/admin").Handler(negroni.New(
Middleware1,
Middleware2,
negroni.Wrap(adminRoutes),
))
看到它们不是嵌套 negroni 实例,而是只创建一个应用于所需路线的实例。
我在 Go 中的 web-application(使用 Gorilla mux
和 negroni
)有大约 20 个处理程序,根据应应用的中间件功能分为三组。具体来说:
第 1 组:静态请求(根本没有中间件)
GET /favicon.ico GET /files GET /files/index.html GET /files/favicon.ico
第 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
这是我的代码,sets-up HTTP 服务器:
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's documentation 中的 "Route Specific Middleware" 部分,我认为这是正确的,其中写着:
If you have a route group of routes that need specific middleware to be executed, you can simply create a new Negroni instance and use it as your route handler.
但是,当我发出请求并使用调试器时,我发现 (*Negroni).ServeHTTP
被多次调用。例如,如果我请求 GET /favicon.ico
那么 staticHandler
函数被正确调用并调用 WriteHeader(200)
,但之后它会调用下一个 mux.Router
调用 WriteHeader(404)
因为 header 被写入了两次 (http: multiple response.WriteHeader calls
)
如果它用于不存在的路由,则 Gorilla 默认值 NotFoundHandler
被调用 3 次(每个 mux.Router
一次)。
如何让 Negroni 在请求完成后停止调用其他处理程序?
...如果我错误配置了我的 Negroni 实例,为什么它不在初始化期间执行检查以警告我配置无效?
我的理解是 negroni.Use
和 UseFunc
是针对 setting-up 中间件(每个请求都会调用),而 UseHandler
是针对 set-up 终端处理程序(每个请求只调用 1 个,或回退到 404)。如果我正确理解了这种情况,那么出于某种原因它将我的终端处理程序视为中间件。
来自 UseHandler
文档 (https://godoc.org/github.com/urfave/negroni#Negroni.UseHandler)
UseHandler adds a http.Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni.
所以您在这里看到的似乎是预期的行为。
您基本上是在创建不同的 negroni 实例并将它们链接起来,因此您的最终 negroniStack
本身就是一个中间件,它将执行您添加的其他中间件。
我相信您想做的是使用实际路由器创建路由,然后将适当的中间件(使用 negroni)添加到每个路由。
如果您查看从文档链接的示例,这就是他们在该部分所做的事情 (https://github.com/urfave/negroni#route-specific-middleware)。
router.PathPrefix("/admin").Handler(negroni.New(
Middleware1,
Middleware2,
negroni.Wrap(adminRoutes),
))
看到它们不是嵌套 negroni 实例,而是只创建一个应用于所需路线的实例。