前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >「Go框架」剖析iris中错误码路由的运行机制

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

作者头像
Go学堂
发布2023-01-31 16:34:36
6310
发布2023-01-31 16:34:36
举报
文章被收录于专栏:Go工具箱

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

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

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

一、错误码路由Demo

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

代码语言:javascript
复制
 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进行错误码路由注册,如下:

代码语言:javascript
复制
 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结构体。具体可参考 通过分析gin、beego源码,读懂web框架对http请求处理流程的本质

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

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

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

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

代码语言:javascript
复制
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)这段的逻辑,其实现如下:

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

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

代码语言:javascript
复制
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)。咱们再继续看该函数的实现,如下:

代码语言:javascript
复制
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-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Go学堂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、错误码路由Demo
  • 二、错误码路由注册
  • 三、iris请求处理流程
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档