英文:
Can a custom HTTP handler be used globally when using Negroni or only per request?
问题
为了确保所有请求的错误结果都能正确处理,我正在实现一个自定义处理程序,如 http://blog.golang.org/error-handling-and-go 中所述。所以,处理程序不仅接受 w http.ResponseWriter, r *http.Request
参数,还可以选择性地返回一个 error
。
我正在使用 Negroni,并想知道是否可以设置一次以将所有请求包装到 handler
中,还是每个请求都必须像下面的示例中的 /
和 /foo
一样进行设置?
type handler func(w http.ResponseWriter, r *http.Request) error
// ServeHTTP 检查错误结果并进行全局处理
func (fn handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err := fn(w, r); err != nil {
http.Error(w, err, http.StatusInternalServerError)
}
}
// Index 匹配 `handler` 类型并返回一个错误
func Index(w http.ResponseWriter, r *http.Request) error {
return errors.New("something went wrong")
}
func main() {
router := mux.NewRouter()
// 注意如何将 `Index` 包装到 `handler` 中。有没有一种方法可以使其全局化?或者每个请求都需要使用 handler(fn) 模式?
router.Handle("/", handler(Index)).Methods("GET")
router.Handle("/foo", handler(Index)).Methods("GET")
n := negroni.New(
negroni.NewRecovery(),
negroni.NewLogger(),
negroni.Wrap(router),
)
port := os.Getenv("PORT")
n.Run(":" + port)
}
英文:
To make sure error results are handled correctly across all requests I'm implementing a custom handler as described in http://blog.golang.org/error-handling-and-go. So instead of only accepting the w http.ResponseWriter, r *http.Request
params the handler optionally returns an error
.
I'm using Negroni and wondered whether I can set it up once to wrap all requests into handler
or if it will always have to be set up on a per-request basis as done for /
and /foo
in the following example?
type handler func(w http.ResponseWriter, r *http.Request) error
// ServeHTTP checks for error results and handles them globally
func (fn handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err := fn(w, r); err != nil {
http.Error(w, err, http.StatusInternalServerError)
}
}
// Index matches the `handler` type and returns an error
func Index(w http.ResponseWriter, r *http.Request) error {
return errors.New("something went wrong")
}
func main() {
router := mux.NewRouter()
// note how `Index` is wrapped into `handler`. Is there a way to
// make this global? Or will the handler(fn) pattern be required
// for every request?
router.Handle("/", handler(Index)).Methods("GET")
router.Handle("/foo", handler(Index)).Methods("GET")
n := negroni.New(
negroni.NewRecovery(),
negroni.NewLogger(),
negroni.Wrap(router),
)
port := os.Getenv("PORT")
n.Run(":" + port)
}
答案1
得分: 0
如果你想的话,可以围绕r.Handle
编写一个包装器。但是不能在全局范围内使用Negroni,因为你使用的中间件不都假设你的handler
类型。
例如:
// 为了清晰起见,命名为wrap。
func wrap(r *mux.Router, pattern string, h handler) *mux.Route {
return r.Handle(pattern, h)
}
func index(w http.ResponseWriter, r *http.Request) error {
io.WriteString(w, "Hello")
return nil
}
func main() {
r := mux.NewRouter()
wrap(r, "/", index)
http.ListenAndServe(":8000", r)
}
我认为这与显式类型转换(清晰但有点重复)或将处理程序类型转换为结构体并没有太大区别。后者可以在以后扩展,以包含线程安全字段(例如数据库连接池、应用程序配置等),然后可以显式地将其传递给每个处理程序。
实际上,你当前的路由器代码仍然清晰易读,并且可以明确地告诉其他人你的处理程序的基础类型。
英文:
You can write a wrapper around r.Handle
if you want. You can't do it globally with Negroni as not all middleware you use assumes your handler
type.
e.g.
// Named to make the example clear.
func wrap(r *mux.Router, pattern string, h handler) *mux.Route {
return r.Handle(pattern, h)
}
func index(w http.ResponseWriter, r *http.Request) error {
io.WriteString(w, "Hello")
return nil
}
func main() {
r := mux.NewRouter()
wrap(r, "/", index)
http.ListenAndServe(":8000", r)
}
I'd argue that this is not much better than just explicitly type-casting your handlers (which is clear, if a little repetitive), or turning your handler type into a struct. The latter you can later extend to contain thread-safe fields (your DB pool, app config, etc) that you can then explicitly pass alongside each handler).
In reality your current router code is still clear and easy to read, and makes it obvious (to others) what type underpins your handlers.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论