内容简介:Echo 倡导通过从中间件和 Handler 返回错误来进行集中式 HTTP 错误处理。集中式错误处理程序使我们能够从统一位置将错误记录到外部服务,并向客户端发送自定义的 HTTP 响应。本节一起学习如何定制化,进行集中式错误处理。在定制篇 4 中,我们讲到了 Render。如果在 Handler 中调用了 Render,但是并没有给 Echo.Renderer 赋值或其他原因,导致出错,页面看到的信息如下:这是 Echo 默认的 HTTP Error Handler,表示发生了 500 – Interna
Echo 倡导通过从中间件和 Handler 返回错误来进行集中式 HTTP 错误处理。集中式错误处理程序使我们能够从统一位置将错误记录到外部服务,并向客户端发送自定义的 HTTP 响应。本节一起学习如何定制化,进行集中式错误处理。
默认的错误处理
在定制篇 4 中,我们讲到了 Render。如果在 Handler 中调用了 Render,但是并没有给 Echo.Renderer 赋值或其他原因,导致出错,页面看到的信息如下:
{ "message": "Internal Server Error" }
这是 Echo 默认的 HTTP Error Handler,表示发生了 500 – Internal Server Error 错误。看看该默认 Error Handler 的代码:
// DefaultHTTPErrorHandler is the default HTTP error handler. It sends a JSON response // with status code. func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) { he, ok := err.(*HTTPError) if ok { if he.Internal != nil { if herr, ok := he.Internal.(*HTTPError); ok { he = herr } } } else { he = &HTTPError{ Code: http.StatusInternalServerError, Message: http.StatusText(http.StatusInternalServerError), } } // Issue #1426 code := he.Code message := he.Message if e.Debug { message = err.Error() } else if m, ok := message.(string); ok { message = Map{"message": m} } // Send response if !c.Response().Committed { if c.Request().Method == http.MethodHead { // Issue #608 err = c.NoContent(he.Code) } else { err = c.JSON(code, message) } if err != nil { e.Logger.Error(err) } } }
- 如果是非 HTTPError,会包装成 HTTPError,并设置为 500 – Internal Server Error;
- 如果开启了 Debug 模式,则会输出具体的错误信息;
因此,开启 Debug 模式(e.Debug = true)后,输出是:
"renderer not registered"
这对于调试很方便。
集中化自定义错误处理
在有些时候,默认错误 HTTP 处理程序可能足够了;但是,如果你想捕获其他类型的错误并采取相应的处理,比如,发送通知电子邮件或将错误记录到外部日志系统中,这时集中化自定义错误处理就很有用了。除此之外,你可能还希望错误页面更好看,或发送个性化的 JSON 响应。
通过给 Echo#HTTPErrorHandler 设置一个 HTTPErrorHandler 来设置自定义的错误处理程序,HTTPErrorHandler 的定义如下:
// HTTPErrorHandler is a centralized HTTP error handler. type HTTPErrorHandler func(error, Context)
自定义错误页面
一般来说,通过 Go 语言渲染页面的话,在发生错误时,渲染对应的错误页面,比如:404.html、500.html 等。
以下自定义 HTTP 错误处理程序根据不同类型错误的显示对应的错误页面并记录错误。
// 放在 http 包下的 error.go 中 // CustomHTTPErrorHandler 自定义 HTTP 错误处理 func CustomHTTPErrorHandler(err error, ctx echo.Context) { code := http.StatusInternalServerError if he, ok := err.(*echo.HTTPError); ok { code = he.Code } errorPage := fmt.Sprintf("%d.html", code) if err := ctx.File("template/" + errorPage); err != nil { ctx.Logger().Error(err) } ctx.Logger().Error(err) }
在项目的 template 目录下加上响应的 html 文件,比如:404.html、500.html。之后设置上自定义的 CustomHTTPErrorHandler:
e.HTTPErrorHandler = myhttp.CustomHTTPErrorHandler
页面简单的包含一些内容,如 404.html 页面内容如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>404</title> </head> <body> <h2>这是404错误页面</h2> </body> </html>
编译启动程序后,随意访问一个不存在的路径:http://localhost:2020/notfound ,结果如下:
这里的错误,我们简单的记录下来。实际业务中,根据你的情况,可以发送到你需要记录的第三方系统中。
另外,这里给客户端的响应,不一定是 HTML 页面,还有可能是 JSON,根据实际的情况进行响应的处理。而且,实际中,HTML 页面也不会是这么简单的一个页面,可能涉及到一些通用数据,比如通用的头尾等。比如让这个错误页面更符合我们网站的风格,可以这么做:
// CustomHTTPErrorHandler 自定义 HTTP 错误处理 func CustomHTTPErrorHandler(err error, ctx echo.Context) { code := http.StatusInternalServerError if he, ok := err.(*echo.HTTPError); ok { code = he.Code } errorPage := fmt.Sprintf("%d.html", code) if err := ctx.Render(code, errorPage, nil); err != nil { ctx.Logger().Error(err) } ctx.Logger().Error(err) }
404.html 页面内容变为:
{{define "content"}} <div> <article> <h2>你想要的页面走丢了!</h2> <hr> 404 </article> </div> {{end}}
这时 404 页面看起来像这样:
怎么做到的?
为什么这么一句:
e.HTTPErrorHandler = myhttp.CustomHTTPErrorHandler
就可以接管错误处理?在 Echo 框架源码中搜索 Echo#HTTPErrorHandler
在哪里调用的:
// ServeHTTP implements `http.Handler` interface, which serves HTTP requests. func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Acquire context c := e.pool.Get().(*context) c.Reset(r, w) h := NotFoundHandler if e.premiddleware == nil { e.findRouter(r.Host).Find(r.Method, getPath(r), c) h = c.Handler() h = applyMiddleware(h, e.middleware...) } else { h = func(c Context) error { e.findRouter(r.Host).Find(r.Method, getPath(r), c) h := c.Handler() h = applyMiddleware(h, e.middleware...) return h(c) } h = applyMiddleware(h, e.premiddleware...) } // Execute chain if err := h(c); err != nil { e.HTTPErrorHandler(err, c) } // Release context e.pool.Put(c) }
以上代码的逻辑大体是:
e.HTTPErrorHandler
如果中间件(包括 Handler)执行没有出错,HTTPErrorHandler 自然执行不到。因此,我们可以不用上文提到的方式进行集中错误处理。 你知道是什么方式吗? 当然我们应该使用 HTTPErrorHandler 的方式,这是使用该框架的正确姿势。
欢迎关注我的公众号:
以上所述就是小编给大家介绍的《Echo系列教程 — 定制篇5:自定义 HTTP Error Handler,让 HTTP 错误处理更友好》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Echo 系列教程 — 定制篇2:自定义 Validator,进行输入校验
- Echo系列教程 — 定制篇4:自定义 Renderer,增强或替换模板引擎
- Echo 系列教程 — 定制篇 1:自定义 Binder,处理 Request 数据绑定
- Echo系列教程 — 定制篇3:自定义 Logger,用你喜欢的日志库
- 为每个虚机度身定制,VMware服务定义防火墙让安全不再被动
- Echo系列教程 — 定制篇6:自定义 Server 相关,替换或扩展默认的 Server
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。