Go Gorilla恐慌处理程序以自定义状态响应

huangapple go评论82阅读模式
英文:

Go Gorilla panic handler to respond with custom status

问题

在Gorilla中,使用RecoveryHandler可以抑制恐慌。但是,是否有处理程序或库方法可以根据给定的错误类型响应特定的HTTP状态代码和消息呢?
例如,在出现“必填字段缺失错误”的情况下,希望响应“Http 400”并提供有关有效负载出错的具体信息。

推荐的方法是什么?
更新
在代码中列出了两种方法:

  1. 处理每个方法调用返回的错误并构建响应。
  2. 不返回错误,而是使用自定义错误类型引发恐慌,并将错误恢复推迟到一个函数中以构建响应。这样可以使代码易于阅读且不重复。
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

  1. Handle errors returned at each method call and build the response.
  2. 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 类型用状态码包装错误,但仍然允许你轻松地检查/记录/写出包装的错误。

进一步阅读:

英文:

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 &amp;StatusError{400, err}
}
err := file.validate()
if err != nil {
return &amp;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:

huangapple
  • 本文由 发表于 2015年11月25日 05:48:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/33904503.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定