黑魔法之Gin框架正则表达式路由

5,728 阅读3分钟

最近遇到在 Gin 框架需要使用正则表达式路由的场景,没有搜到合适的开源实现,就自己轮了一个,分享给大家,有类似需求的朋友可以直接使用或参考实现自己的特殊需求。

如果觉得不错,不要忘了给个Star哈 ~

Repo

github.com/jxskiss/gin…

Why

在Github上,用户多次提出了支持正则路由的需求,但是一直都没有支持,有兴趣的同学可以去官方的 issue 列表看看。

Gin宣传自己是一个高性能的Web框架,其高性能主要来源于所使用的基于前缀树的 httprouter,httprouter 不支持正则表达式,也没有支持计划。那正则路由真的是个伪需求吗,为啥要基于Gin实现一个正则路由呢?

有一个老掉牙的故事……

某天开始,本人接手维护一个经历过多个团队的古老的Python服务,在一系列的优化后,性能问题和项目的可维护性仍然让人头疼。维护这种经年累月、很多历史债务的动态类型语言的项目,真是一言难尽啊。

另一方面,随着时间推移,公司的主要技术栈已经转向了Go语言,各种基础建设和工具库的支持都比Python更加完善,性能也比Python优秀很多。所以从长期来说,使用Go语言逐步重构这个项目是一个比较合理的迭代方向。但是重构不是一蹴而就的,重构的过程中,我们还必须要保持服务的稳定运行以及已经发布到终端用户的客户端接口的兼容性。

所以我们首先构建了一个Gateway服务,接管了请求入口,然后我们逐步的重构接口,重构使用Go语言编写,一部分是直接写在Gateway服务里的,还没有重构的接口,在Gateway里把请求反向代理到旧的Python服务。这里就遇到问题了:

  1. 系统有上百个接口,反向代理需要一个一个配置?

  2. Python (Flask) 支持的路由规则非常灵活,有些路由在Gin里不支持,比如下面这组,虽然不合理但是确实就是这么配置的:

    • /users/settings/ 查询和修改当前登录用户的设置
    • /users/some_biz_data/ 查询当前登录用户的某项业务数据
    • /users/<int:user_id>/ 查询指定ID用户的个人信息

    这组接口在Gin框架中是不支持的,注册路由时会产生冲突。

因此,使用正则表达式路由是一个比较合理的选择。

Usage

安装:

go get github.com/jxskiss/ginregex

使用示例:

r := gin.Default()

// Configure your normal non regular expression routes.
r.GET("/path_1", handler1)
r.POST("/path_2", handler2)

// Optionally you may configure your NoRoute handler, it won't affect the regex router.
r.NoRoute(noRouteHandler)

// Configure your regular expression routes.
regexRouter := ginregex.New(r, nil)
regexRouter.GET("^/.*$", regexHandler)

r.Run(":8080")

一些补充说明:

  1. 这个正则路由的实现跟Gin框架的 NoRoute 特性是兼容的,用户可以像之前一样可选地注册 NoRoute;
  2. 注册普通路由和正则表达式路由的顺序不重要;
  3. 先后注册的正则表达式路由必须是唯一的,注册重复的正则路由会引发panic;
  4. 注册在 gin.EngineRegexRouter 上的中间件会被组合到最终的正则路由处理函数中;
  5. 如果正则表达式中有命名捕获分组(named capturing group),匹配的内容会被填充到 gin.Context.Params 中,用户可以想普通路由中的参数一样,在请求处理函数和参数绑定函数中使用;
  6. 可选地,在创建 RegexRouter 路由时,用户可以指定一个Hook函数,请求路径被正则表达式匹配到以后,Hook会被调用;用户可以使用这个特性来做一些跟其他Gin中间件的集成工作,比如通过 gin.Context.Keys 传递一些上下文信息等;

仓库地址:github.com/jxskiss/gin…

欢迎Star, 报bug,提需求 ~