Go 请求完成后,Negroni继续调用其他处理程序

Go 请求完成后,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

我在Go中的web应用程序(使用Gorilla
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
    
这是我设置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实例并将其用作路由处理程序

但是,当我发出请求并使用调试器时,我看到
(*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),
))