英文:
Go Gorilla panic handler to respond with custom status
问题
在Gorilla中,使用RecoveryHandler可以抑制恐慌。但是,是否有处理程序或库方法可以根据给定的错误类型响应特定的HTTP状态代码和消息呢?
例如,在出现“必填字段缺失错误”的情况下,希望响应“Http 400”并提供有关有效负载出错的具体信息。
推荐的方法是什么?
更新
在代码中列出了两种方法:
- 处理每个方法调用返回的错误并构建响应。
- 不返回错误,而是使用自定义错误类型引发恐慌,并将错误恢复推迟到一个函数中以构建响应。这样可以使代码易于阅读且不重复。
func fooHandler(w http.ResponseWriter, r *http.Request) {
// 将请求体解码为结构体实例
if err := decode(r, myInstance); err != nil {
sendErrorResponse(w,err,http.StatusBadRequest)
return
}
// 验证结构体实例中是否存在所有必填键
if err := file.validate(); err != nil {
sendErrorResponse(w,err,http.StatusBadRequest)
return
}
// 调用数据库并验证响应并处理错误
// 进行一些计算并再次处理错误。
// 最后构造响应
}
func barHandler(w http.ResponseWriter, r *http.Request) {
// 在构造响应之前,与上述处理程序类似,调用了许多函数
}
func tomHandler(w http.ResponseWriter, r *http.Request) {
// 在构造响应之前,与上述处理程序类似,调用了许多函数
}
func differentHandler(w http.ResponseWriter, r *http.Request) {
defer recoverForErrors(w,r)
// 调用任意数量的函数。
// 验证、解码等会引发恐慌而不是返回错误。
// 这将避免处理错误和转换为有意义的错误响应的重复样板代码
// 而是将所有这些逻辑推到recoverForErrors函数中。该函数从恐慌中检索错误并检查特定的错误类型以构造错误的HTTP响应
}
英文:
In Gorilla, using RecoveryHandler we could suppress the panics. However is there a handler or a library method to respond with a specific Http status code and message for given error type.<br>
For example, in case of a Panic for Mandatory field missing error
, one would want to respond with Http 400
and a meaningful message of what exactly is wrong with the payload.<br>
What is the recommended approach to do this? <br>
UPDATE
In code: 2 approaches are listed
- Handle errors returned at each method call and build the response.
- Instead of returning errors, panic with custom error types and defer the error recovery to a func to build the response. This makes the code easy to read and less repetitive.
<!-- language: go -->
func fooHandler(w http.ResponseWriter, r *http.Request) {
//decode the request body into a struct instance
if err := decode(r, myInstance); err != nil {
sendErrorResponse(w,err,http.StatusBadRequest)
return
}
//validate the struct instance for all mandatory keys presence
if err := file.validate(); err != nil {
sendErrorResponse(w,err,http.StatusBadRequest)
return
}
//call DB and validate the response and handle the error
//do some computation and again handle error.
//finally construct response
}
func barHandler(w http.ResponseWriter, r *http.Request) {
//similar to above handler many funcs are called before the response is contruscted
}
func tomHandler(w http.ResponseWriter, r *http.Request) {
//similar to above handler many funcs are called before the response is contruscted
}
func differentHandler(w http.ResponseWriter, r *http.Request) {
defer recoverForErrors(w,r)
// call as many funcs as you need.
// validation, decoding etc will panic instead of returning errors.
// This will avoid the repetitive boiler plate code of handling error and converting to meaningful error response
// instead all this logic is pushed to recoverForErrors func. Which retrieves the error from panic and checks for
// specific error type to construct the error http response
}
答案1
得分: 4
尽可能地依赖标准库提供的接口是惯用的做法。在这种情况下,可以使用 net/http 包中的 http.Handler
接口。
在你的情况下,你可以创建一个新类型,允许你的处理程序返回一个错误类型,并集中处理所有这些错误情况。
// StatusError 用 HTTP 状态码包装现有的错误。
type StatusError struct {
Status int
// 允许你包装另一个错误
Err error
}
func (e *StatusError) Error() string {
return e.Error()
}
type AppHandler func(w http.ResponseWriter, r *http.Request) error
// 满足 http.Handler 接口
func (ah AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// 集中处理错误
err := ah(w, r)
if err != nil {
switch e := a.(type) {
case *StatusError:
switch e.Status {
case 400:
http.Error(w, e.Err.Error(), 400)
return
case 404:
http.NotFound(w, r)
return
default:
http.Error(w, http.StatusText(500), 500)
return
}
default:
http.Error(w, http.StatusText(500), 500)
return
}
}
}
// 你的处理程序将如下所示
func SomeHandler(w http.ResponseWriter, r *http.Request) error {
err := decode(r, myInstance)
if err != nil {
return &StatusError{400, err}
}
err := file.validate()
if err != nil {
return &StatusError{400, err}
}
// 继续处理...
return nil
}
这样做的好处包括:
- 对于可以处理的错误,不会发生 panic。
- 可以在
ServeHTTP
方法中集中处理错误,例如对于 400 错误,可以将错误原因写入响应。对于 500 错误,可以返回一个通用消息,因为用户无法解决 HTTP 500 错误。 - 处理程序函数明确返回错误,不再需要记住使用裸露的
return
语句来避免继续执行。 StatusError
类型用状态码包装错误,但仍然允许你轻松地检查/记录/写出包装的错误。
进一步阅读:
- http://blog.golang.org/error-handling-and-go
- http://elithrar.github.io/article/http-handler-error-handling-revisited/
- http://dave.cheney.net/2014/12/24/inspecting-errors
英文:
It is idiomatic to lean on the interfaces provided by the standard library as much as possible. In this case, the http.Handler
interface from the net/http package.
In your case, you can create a new type that allows your handlers to return an error type, and handle all of those error cases centrally.
// StatusError wraps an existing error with a HTTP status code.
type StatusError struct {
Status int
// Allows you to wrap another error
Err error
}
func (e *StatusError) Error() string {
return e.Error()
}
type AppHandler func(w http.ResponseWriter, r *http.Request) error
// Satisfies the http.Handler interface
func (ah AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Centralises your error handling
err := ah(w, r)
if err != nil {
switch e := a.(type) {
case *StatusError:
switch e.Status {
case 400:
http.Error(w, e.Err.Error(), 400)
return
case 404:
http.NotFound(w, r)
return
default:
http.Error(w, http.StatusText(500), 500)
return
}
default:
http.Error(w, http.StatusText(500), 500)
return
}
}
// Your handlers will look like this
func SomeHandler(w http.ResponseWriter, r *http.Request) error {
err := decode(r, myInstance)
if err != nil {
return &StatusError{400, err}
}
err := file.validate()
if err != nil {
return &StatusError{400, err}
}
// Continue on...
return nil
}
The benefits you get here include:
- No panicking for errors that can be handled
- You can centralise your error handling in your
ServeHTTP
method - i.e. for 400 errors, you might write the error reason to the response. For 500 errors, you might return a generic message since a HTTP 500 isn't something the user can be expected to solve. - Your handler functions return errors explicitly, and you no longer need to remember to use naked
return
statements to avoid continued execution. - Your
StatusError
type wraps the error with a status code, but still allows you to inspect/log/write out the wrapped error easily.
Further reading:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论