英文:
Golang HTTP custom error handling response
问题
我正在阅读https://blog.golang.org/error-handling-and-go,最后给出了一个处理返回错误的更简洁方式的好例子,并且只是做了一些简单的事情:
// 处理函数的包装器。
type rootHandler func(http.ResponseWriter, *http.Request) error
// 实现http.Handler接口。
func (fn rootHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
err := fn(w, r) // 调用处理函数。
if err == nil {
return
}
...
}
// POST /api/test/
testRouter.
Handle("/", rootHandler(create)).
Methods("POST")
// GET /api/test/{id}/
testRouter.
HandleFunc("/{id:[0-9]+}/", rootHandler(getByID)).
Methods("GET")
func create(w http.ResponseWriter, r *http.Request) error {
// CustomError 实现了 error 接口
return &CustomError{
Kind: EPARSE,
Status: http.StatusBadRequest,
Message: "some message",
Op: "create",
Err: err,
}
}
这个方法非常有效,但我希望不要在每个控制器方法(在这种情况下是create
)中包装rootHandler
,并且想出了最好的方法是找出某种后置中间件。我尝试创建一个后置中间件,让路由器使用它而不是每个控制器方法,但是失败了,并想知道您如何实现这一点。我在SO上找到的最接近的答案是emmbee在https://stackoverflow.com/questions/42871194/how-can-i-combine-go-middleware-pattern-with-error-returning-request-handlers上的答案,除了AuthMiddleware中的fn将是一个控制器方法。
所以理想情况下,我希望有下面的处理程序,如果存在CustomError
,则处理它:
// POST /api/test
testRouter.
Handle("/", create).
Methods("POST")
为了背景,我正在使用gorilla mux和negroni。
非常感谢您的任何想法!非常感谢。
英文:
I was going through https://blog.golang.org/error-handling-and-go and at the end it gave a good example on how to handle returning errors in a cleaner way and just made something simple:
// Wrapper for handler functions.
type rootHandler func(http.ResponseWriter, *http.Request) error
// Implement the http.Handler interface.
func (fn rootHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
err := fn(w, r) // Call handler function.
if err == nil {
return
}
...
}
// POST /api/test/
testRouter.
Handle("/", rootHandler(create)).
Methods("POST")
// GET /api/test/{id}/
testRouter.
HandleFunc("/{id:[0-9]+}/", rootHandler(getByID)).
Methods("GET")
func create(w http.ResponseWriter, r *http.Request) error {
// CustomError implementes error
return &CustomError{
Kind: EPARSE,
Status: http.StatusBadRequest,
Message: "some message",
Op: "create",
Err: err,
}
}
This worked very well, but I would prefer not to wrap every controller method (create
in this case) in rootHandler
, and figured the best way is to figure out some sort of post-middleware. I've failed at trying to create a post-middlewhere which the router uses instead of each controller method, and wondering how you may go about implementing this. The closest answer on SO I could find was emmbee's answer on https://stackoverflow.com/questions/42871194/how-can-i-combine-go-middleware-pattern-with-error-returning-request-handlers except for fn in AuthMiddleware would be a controller method.
So ideally, I would have the below handler which handles the CustomError
if it exists
// POST /api/test
testRouter.
Handle("/", create).
Methods("POST")
For context, I'm using gorilla mux and negroni.
Any ideas are appreciated! Thank you very much.
答案1
得分: 4
你的理想解决方案行不通。mux API支持http.Handler
和func(http.ResponseWriter, *http.Request)
作为参数,而你有一个func(http.ResponseWriter, *http.Request) error
。func(http.ResponseWriter, *http.Request) error
不能作为mux API支持的参数类型之一。
唯一的解决方案是使用rootHandler
包装器将每个控制器函数适应为mux API支持的类型。与标准的http.HandlerFunc
包装器相比,这个包装器不会增加额外的开销。
使用一个辅助函数来减少包装器所需的代码量:
func handle(r *mux.Router, p string, fn func(http.ResponseWriter, *http.Request) error) *mux.Route {
return r.Handle(p, rootHandler(fn))
}
...
handle(testRouter, "/", create).Methods("POST")
handle(testRouter, "/{id:[0-9]+}/", getByID).Methods("GET")
在辅助函数中使用类型切换来处理不同的控制器类型:
func handle(r *mux.Router, p string, h interface{}) *mux.Route {
switch h := h.(type) {
case func(http.ResponseWriter, *http.Request) error:
return r.Handle(p, rootHandler(h))
case func(http.ResponseWriter, *http.Request):
return r.HandleFunc(p, h)
case http.Handler:
return r.Handle(p, h)
default:
panic(fmt.Sprintf("handler type %T not supported", h))
}
}
英文:
Your ideal solution will not work. The mux API supports http.Handler
and func(http.ResponseWriter, *http.Request)
arguments. You have a func(http.ResponseWriter, *http.Request) error
. A func(http.ResponseWriter, *http.Request) error
cannot be passed as one of the argument types supported by the mux API.
The only solution is to adapt each controller function to a type supported by the mux API using the rootHandler
wrapper. The wrapper does not add overhead compared to the standard http.HandlerFunc
wrapper.
Use a helper function to reduce the amount of code required for the wrappers:
func handle(r *mux.Router, p string, fn func(http.ResponseWriter, *http.Request) error) *mux.Route {
return r.Handle(path, rootHandler(fn))
}
...
handle(testRouter, "/", create).Methods("POST")
handle(testRouter, "/{id:[0-9]+}/", getByID).Methods("GET")
Use a type switch in the helper function to handle different controller types:
func handle(r *mux.Router, p string, h interface{}) *mux.Route {
switch h := h.(type) {
case func(http.ResponseWriter, *http.Request) error:
return r.Handle(p, rootHandler(h))
case func(http.ResponseWriter, *http.Request):
return r.HandleFunc(p, h)
case http.Handler:
return r.Handle(p, h)
default:
panic(fmt.Sprintf("handler type %T not supported", h))
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论