Go Gin框架路由相关bug分析

 更新时间:2023年12月14日 09:40:02   作者:cainmusic  
这篇文章主要为大家介绍了Go Gin框架路由相关bug分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

引言

注:本文原文有错误,原文不改动,但在结尾进行了勘误,注意读到文章结尾。

Gin相关版本v1.9.1

当你按如下方法注册两个路由的时候,bug会发生。

r := gin.Default()
    r.GET("/static/", func(c *gin.Context) { c.String(200, "static") })
    r.GET("/static/*file", func(c *gin.Context) { c.String(200, "static file") })
    r.Run()

上面的代码会报错:

panic: runtime error: index out of range [0] with length 0

分析

虽然构建路由树的时候,Gin本身就会主动产生很多panic,但上面这个panic显然是个意外。

这个bug由catchAll通配符的特异性导致。

catchAll通配符虽然写作*paramname,但其构建路由树的时候会向前匹配一位/

因为catchAll通配符通常是为了匹配路径而存在的,catchAll通配符在gin中的经典应用就是配置静态文件服务器。

参考gin项目的routergroup.go文件:

func (group *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) IRoutes {
    if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
        panic("URL parameters can not be used when serving a static folder")
    }
    handler := group.createStaticHandler(relativePath, fs)
    urlPattern := path.Join(relativePath, "/*filepath")
    // Register GET and HEAD handlers
    group.GET(urlPattern, handler)
    group.HEAD(urlPattern, handler)
    return group.returnObj()
}

一旦你使用Static相关函数配置静态文件服务,最后都会调用到上面的方法。

其中用你传入的relativePath/*filepath组合为最终的url:relativePath/*filepath

假如你传入的路径是/static,则最终url为/static/*filepath

这个路由会匹配所有以/static/开头的url,并将后面的所有内容赋值到filepath

没错,是所有,包括后面的/,比如html/group1/page1.html,也就是说可以通过filepath访问到子目录。

但上面描述的内容实际上有一个错误,你以为filepath保存的内容是html/group1/page1.html

实际上是/html/group1/page1.html

catchAll通配符会尝试向前多匹配一个/,如果你的路由中没有这个/,会报错。

这个特性的特异之处导致gin中有一个bug,就是当你已经注册了/static/路由之后,再注册/static/*file的时候,我们会在/static/节点上插入*filepath而不是/*filepath,这导致在程序判断这是一个catchAll路由后,会去向前匹配一位/,这时i--后,变成了负数,就导致了index out of range的错误。

你可能会觉得报错是对的啊,那么你需要注意分清报错和bug产生的panic。

i--
if path[i] != '/' {
    panic("no / before catch-all in path '" + fullPath + "'")
}

我说的报错产生在panic("no / before catch-all in path '" + fullPath + "'")

而bug产生的panic产生在i--变为负数后查询if path[i] != '/'时。

简单处理的话,这里应该对i的值进行判断,然后主动panic抛出可以让人领悟的报错。

当然,为了解决这个问题本身,可以将上面的代码修改为:

r := gin.Default()
    r.GET("/static", func(c *gin.Context) { c.String(200, "static") })
    r.GET("/static/*file", func(c *gin.Context) { c.String(200, "static file") })
    r.Run()

第一个路由不要加末尾的/,就可以规避这个bug。

勘误

前文提到panic: runtime error: index out of range [0] with length 0报错,但这显然不是index为负的报错,是我之前预判i--为负时一厢情愿了。

实际上这个错误发生在i--的上面几行:

if len(n.path) > 0 && n.path[len(n.path)-1] == '/' {
    pathSeg := strings.SplitN(n.children[0].path, "/", 2)[0]
    panic("catch-all wildcard '" + path +
        "' in new path '" + fullPath +
        "' conflicts with existing path segment '" + pathSeg +
        "' in existing prefix '" + n.path + pathSeg +
        "'")
}
// currently fixed width 1 for '/'
i--
if path[i] != '/' {
    panic("no / before catch-all in path '" + fullPath + "'")
}

这一行:pathSeg := strings.SplitN(n.children[0].path, "/", 2)[0]

这里的children长度实际为0,但这里却默认children有内容。

看panic报错的内容:catch-all wildcard "path" in new path "fullPath" conflicts with existing path segment "pathSeg" in existing prefix "n.path" + "pathSeg"

大意上还是catchAll通配符和当前路径冲突,但这里Gin默认此时n.children不为空的逻辑我还是没太想明白。

以上就是Go Gin框架路由相关bug分析的详细内容,更多关于Go Gin框架路由bug的资料请关注脚本之家其它相关文章!

相关文章

  • 详解Golang中interface接口的原理和使用技巧

    详解Golang中interface接口的原理和使用技巧

    interface 接口在 Go 语言里面的地位非常重要,是一个非常重要的数据结构。本文主要介绍了Golang中interface接口的原理和使用技巧,希望对大家有所帮助
    2022-11-11
  • 如何使用 Go 获取你的 IP 地址(推荐)

    如何使用 Go 获取你的 IP 地址(推荐)

    在Go语言中,获取IP地址分为公共IP和私有IP两种方式,公共IP地址通过外部API获取,本文给大家介绍如何使用 Go 获取你的 IP 地址,感兴趣的朋友跟随小编一起看看吧
    2024-09-09
  • GoLand安装与环境配置的完整步骤

    GoLand安装与环境配置的完整步骤

    作为一个go语言程序员,觉得自己有义务为go新手开一条更简单便捷的上手之路,下面这篇文章主要给大家介绍了关于GoLand安装与环境配置的完整步骤,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2022-12-12
  • 一文教你如何优雅处理Golang中的异常

    一文教你如何优雅处理Golang中的异常

    我们在使用Golang时,不可避免会遇到异常情况的处理,与Java、Python等语言不同的是,Go中并没有try...catch...这样的语句块,这个时候我们如何才能更好的处理异常呢?本文来教你正确方法
    2022-11-11
  • viper配置框架的介绍支持zookeeper的读取和监听

    viper配置框架的介绍支持zookeeper的读取和监听

    这篇文章主要介绍了viper配置框架的介绍支持zookeeper的读取和监听,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-05-05
  • Go语言实战学习之流程控制详解

    Go语言实战学习之流程控制详解

    这篇文章主要为大家详细介绍了Go语言中的流程控制,文中的示例代码讲解详细,对我们学习Go语言有一定的帮助 ,需要的朋友可以参考下
    2022-08-08
  • Golang int函数使用实例全面教程

    Golang int函数使用实例全面教程

    这篇文章主要为大家介绍了Golang int函数使用实例全面教程详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10
  • goland安装1.7版本报错Unpacked SDK is corrupted解决

    goland安装1.7版本报错Unpacked SDK is corrupted解决

    这篇文章主要为大家介绍了goland安装1.7版本报错Unpacked SDK is corrupted解决,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • Go语言标准库sync.Once使用场景及性能优化详解

    Go语言标准库sync.Once使用场景及性能优化详解

    这篇文章主要为大家介绍了Go语言标准库sync.Once使用场景及性能优化详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • Golang配置解析神器go viper使用详解

    Golang配置解析神器go viper使用详解

    viper是一个很完善的Go项目配置解决方案,很多著名的开源项目都在使用,比如Hugo,Docker都使用了该库,使用viper可以让我们专注于自己的项目代码,而不用自己写那些配置解析代码,本文给大家介绍Golang配置解析神器go viper使用,感兴趣的朋友一起看看吧
    2022-05-05

最新评论