写点什么

「Go 框架」剖析 iris 中错误码路由的运行机制

作者:Go学堂
  • 2023-01-19
    北京
  • 本文字数:2163 字

    阅读完需:约 7 分钟

「Go框架」剖析iris中错误码路由的运行机制

大家好,我是渔夫子。本号新推出「Go 工具箱」系列,意在给大家分享使用 go 语言编写的、实用的、好玩的工具。同时了解其底层的实现原理,以便更深入地了解 Go 语言。


在 iris 中,除了能够指定正常的请求路由外,还能根据 http 的响应错误码指定具体的请求处理函数,以便针对具体的错误做出不同的响应。例如,当响应状态码是 400 时,该如何处理该请求;当响应状态码是 500 时,又该如何处理该请求。


本文就 iris 框架中错误码路由的运行机制做一个深入的剖析。

一、错误码路由 Demo

我们先来看下,在 iris 中是如何给特定的响应状态码指定对应的路由函数的。如下:


  app := iris.New()
// 捕获特定的错误码。iris.StatusInternalSErverError=500 app.OnErrorCode(iris.StatusInternalServerError, func(ctx iris.Context) { ctx.HTML("Message: <b>" + ctx.Values().GetString("message") + "</b>") })
app.Get("/my500", func(ctx iris.Context) { ctx.Values().Set("message", "this is the error message") ctx.StatusCode(500) })
// http://localhost:8080/my500 app.Listen(":8080")
复制代码


我们运行服务,然后访问 http://localhost:8080/my500 时,会输出 "Message:** this is the error message**"。我们发现,该路径是先执行 "/my500" 对应的处理函数,然后设置错误码是 500,然后再执行到了 app.OnErrorCode 对应的处理函数中。


接下来我们就分析下 iris 是如何捕获到请求处理函数中对应的错误码的。

二、错误码路由注册

通过 app.OnErrorCode 可以对指定的错误码进行路由注册。根据上文讲解的 iris 路由的结构,在 routerHandler 中,不仅有正常的路由表,而且还有一个专门用于错误处理的路由表字段:errorTrees,如下:



在服务启动前,使用app.OnErrorCode进行错误码路由注册,如下:


  app.OnErrorCode(iris.StatusInternalServerError, func(ctx iris.Context) {    ctx.HTML("Message: <b>" + ctx.Values().GetString("message") + "</b>")  })
复制代码


以上注册的路由,最终生成的路由树如下:



在 iris 中错误码路由和正常的路由树是分开在两个字段存储的。下面接着看 iris 是如何利用这两棵路由树的。

三、iris 请求处理流程

首先,我们看下 iris 框架对请求的整体处理流程。如下图:



在之前的文章中我们详细讲解过 go 的常用 web 框架对 http 请求的本质都是调用标准库中的 net/http 包中的 Server 结构体。具体可参考 。


iris 框架也不例外,在通过 Listen 函数启动服务的逻辑中,给 Server.Handler 指定了 router.Router 作为对应的请求处理入口。



同时,router.Router 又实现了 ServeHTTP 接口。在 Router.ServeHTTP 函数中调用 Router.mainHandler 方法。如下:


func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {  router.mainHandler(w, r)}
复制代码


而 Router.mainHandler 方法的实现如下:


func (router *Router) buildMainHandler(cPool *context.Pool, requestHandler RequestHandler) {  router.mainHandler = func(w http.ResponseWriter, r *http.Request) {    ctx := cPool.Acquire(w, r)    router.requestHandler.HandleRequest(ctx)    cPool.Release(ctx)  }}
复制代码


这里的处理逻辑也很清晰了。


  • 首先从上下文池中获取一个上下文 ctx

  • 处理正常的请求。包括路由匹配、执行具体的请求处理函数、设置响应码。

  • 释放上下文 ctx


我们接着看 cPool.Release(ctx)这段的逻辑,其实现如下:


func (c *Pool) Release(ctx *Context) {  if !ctx.manualRelease {    ctx.EndRequest()    c.pool.Put(ctx)  }}
复制代码


这里有一行是 ctx.EndRequest(),继续看该函数的实现,如下:


func (ctx *Context) EndRequest() {  if !ctx.app.ConfigurationReadOnly().GetDisableAutoFireStatusCode() &&    StatusCodeNotSuccessful(ctx.GetStatusCode()) {    ctx.app.FireErrorCode(ctx)  }
ctx.writer.FlushResponse() ctx.writer.EndResponse()}
复制代码


在该函数中,其中会判断如果当前输出的状态码不是成功状态(即 200),那么就执行 ctx.app.FireErrorCode(ctx)。咱们再继续看该函数的实现,如下:


func (h *routerHandler) FireErrorCode(ctx *context.Context) {  ...    statusCode := ctx.GetStatusCode() // the response's cached one.    ...        for i := range h.errorTrees {    t := h.errorTrees[i]
if statusCode != t.statusCode { continue } ... n := t.search(ctx.Path(), ctx.Params()) if n == nil { // try to take the root's one. n = t.root.getChild(pathSep) }
if n != nil { ctx.SetCurrentRoute(n.Route)
ctx.HandlerIndex(0) ctx.Do(n.Handlers) return }
break }
ctx.Do(h.errorDefaultHandlers)}
复制代码


在该函数中,我们看到会从 routerHandler 的 errorTrees 中进行匹配路由,并执行对应的请求处理逻辑。这里正好就是在一开始的时候根据状态码注册的路由。


好了,以上就是咱们几天要介绍的内容,希望对大家理解 iris 框架有所帮助。


---特别推荐---

特别推荐:一个专注 go 项目实战、项目中踩坑经验及避坑指南、各种好玩的 go 工具的公众号,「Go 学堂」,专注实用性,非常值得大家关注。点击下方公众号卡片,直接关注。关注送《100 个 go 常见的错误》pdf 文档。

发布于: 2023-01-19阅读数: 21
用户头像

Go学堂

关注

微信公众号:Go学堂 2019-08-06 加入

专注Go编程知识、案例、常见错误及原理分析。意在通过阅读更多优秀的代码,提高编程技能。同名公众号「Go学堂」期待你的关注

评论

发布
暂无评论
「Go框架」剖析iris中错误码路由的运行机制_golang_Go学堂_InfoQ写作社区