安恒信息范渊在乌镇峰会谈AI:以工具视之、以工具用之、以工具治理之诺基亚与微软再合作,为 Azure 数据中心供货延长五年天岳先进发布业界首款 300mm(12 英寸)N 型碳化硅衬底三星介绍内部安全团队 Project Infinity 攻防演练项目,高效修复 Galaxy 手机平板漏洞上海市将推进低空飞行服务管理能力建设,2027 年底前累计划设相应航线不少于 400 条岁末,海尔给您备好一套“小红花”为什么说Q10K Pro是今年最值得入手的电视?看完这几点就明白了!“小墨方·大不凡”!Brother“小墨方”系列彩喷一体机全新上市黄仁勋:AI智能需求强劲,“物理定律”限制英伟达芯片增长诺基亚与微软再合作,为Azure数据中心供货延长五年国家数据局:到2029年基本建成国家数据基础设施主体结构中国已发展成为全球最大的互联网市场,拥有全球最多的网民和移动互联网用户中国铁塔:计划按照10:1的比例合股美国FCC正式划定5.9GHz频段用于C-V2X技术在AI领域奋起直追!苹果要对Siri大革新 2026年正式发布日本机构公布量子专利榜单:本源量子、国盾量子位居全球第1中国联通:拟向华为、中兴展开5G网络设备竞争性谈判采购京东“先人一步”预约OPPO Reno13系列 1元锁定腾讯视频VIP会员季卡万兆光网中国行:以应用为牵引,上海万兆加速启航消息称芯片制造商铠侠12月18日上市,将通过IPO筹资总计700亿日元
  • 首页 > 网络通信频道 > 通信技术

    提升Go的HTTP路由器的提案

    2023年08月18日 16:57:27   来源:51CTO

      译者 | 刘汪洋

      Go 的标准库中包含一个稳定且成熟的 HTTP 服务器。然而,内置的请求路由器http.ServeMux 功能较为简洁,因此你常常需要自己编写路由代码。

      其主要短板是,它并未支持 HTTP 方法的匹配(如GET和POST的区别),同时也无法持/users/{user}/settings这种类型的通配符路径。然而,这两个功能几乎是所有 REST 风格的 API 服务器所必需的。

      当然,你可以选择自行实现这些功能。在我以前的一篇文章 Go 中不同的 HTTP 路由方法中,提到过有一些优秀的第三方包可以实现更高级的路由功能,并且只需 约 30 行代码 就能够在不借助任何第三方库的情况下实现类似的功能。

      但是,未来可能不再需要这些替代方案和第三方包。现在有一个 活跃的提案 - 还包括一个旨在改进 ServeMux 参考实现 ,使其能够匹配 HTTP 方法和通配符路径。

      Google 的 Go 团队成员 Jonathan Amsterdam 主导了这个提案以及之前的 讨论。Jonathan 曾成功提出将结构化日志添加到标准库的提案 - Go 1.21 将包含他的log/slog包(预计 2023 年 8 月发布)。

      现状与变革

      在目前的情况下,如果想将 GET 请求匹配到 /users/{user}/settings,你需要编写以下的样板代码(尽管在实践中你可能会使用第三方库):

      复制

      mux.HandleFunc("/users/", func(w http.ResponseWriter, r *http.Request) {

      if r.Method != "GET" {

      http.Error(w, "method not allowed", http.StatusMethodNotAllowed)

      return

      }

      remainder := r.URL.Path[len("/users/"):]

      userId, subPath, _ := strings.Cut(remainder, "/")

      switch subPath {

      case "settings":

      fmt.Fprintf(w, "user %s", userId)

      // 其他子路径可以在这里添加

      default:

      http.NotFound(w, r)

      }

      })

      如果接受了这个提议,你可以更加简单地实现一样的功能:

      复制

      mux.HandleFunc("GET /users/{user}/settings", func(w http.ResponseWriter, r *http.Request) {

      fmt.Fprintf(w, "user %s", r.PathValue("user"))

      })

      这样的写法明显更为简洁!

      这与其他流行路由器使用的语法非常相似:

      复制

      // github.com/go-chi/chi

      router.Get("/users/{user}/settings", func(w http.ResponseWriter, r *http.Request) {

      fmt.Fprintf(w, "user %s", chi.URLParam(r, "slug"))

      })

      // github.com/gorilla/mux

      router.HandleFunc("/users/{user}/settings", func(w http.ResponseWriter, r *http.Request) {

      fmt.Fprintf(w, "user %s", mux.Vars(r)["user"])

      }).Methods("GET")

      // github.com/bmizerany/pat

      router.Get("/users/:user/settings", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

      fmt.Fprintf(w, "user %s", r.URL.Query().Get(":user"))

      }))

      // github.com/gin-gonic/gin

      router.GET("/users/:user/settings", func(c *gin.Context) {

      fmt.Fprintf(w, "user %s", c.Param("user"))

      })

      提案中的一个有趣决定是,并没有为 ServeMux 添加新的方法;而是对现有的 Handle 和 HandleFunc 方法进行了扩展,以支持方法前缀和 {wildcard} 路径段。

      我理解他们避免添加新方法的想法,但我对这个决定持保留态度。遗憾的是,旧版的 ServeMux 接受如 Handle("GET /foo", h) 的模式。这意味着为增强版 ServeMux 编写的代码将在旧版 Go 上能正常编译和运行,但路由不会匹配到任何内容,这容易导致错误。我可能会添加新的方法,比如 HandleMatch / HandleMatchFunc 或 Route / RouteFunc。

      该提议也详细描述了处理两个重叠模式的优先级,其核心规则简单明了:“如果两个模式有重叠(有共同的请求),则更具体的模式优先匹配”。

      例如,如果你注册了模式 /users/(匹配 /users/*)以及模式 /users/{user},当一个 /users/ben 的请求进来时,它将匹配第二个,更具体的模式。这与现有的 ServeMux 中,特定主机的模式优先于没有主机名的模式的行为一致。

      URL 末尾通配符匹配

      此提案为我们带来了一个新的"特殊通配符" {$},它专门用于匹配 URL 的末尾。对于那些仅希望匹配主页路由的情况,这个新特性显得非常实用。在此之前,要实现这一目标颇为麻烦,因为以 / 结尾的模式会匹配所有 / 之下的内容;这个规则对于只有 / 的模式同样适用。

      因此,以前若想匹配主页,你需要这样操作:

      复制

      mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

      if r.URL.Path != "/" { // 确保路径就是 "/"

      http.NotFound(w, r)

      return

      }

      serveHomepage(w, r)

      })

      mux.HandleFunc("/users", serveUsers)

      这一过程颇为繁琐。若你忘记了路径检查,那么你最终可能会将主页用于所有其他的 URL,而不是显示一个未找到的页面,因为所有的内容都在 / 之下。

      而根据新的提案,这个过程将变得更加简洁:

      复制

      mux.HandleFunc("/{$}", serveHomepage)

      mux.HandleFunc("/users", serveUsers)

      实现参考

      Jonathan 在 github.com/jba/muxpatterns 中发布了一个 ServeMux 的增强版本的示例实现。唯一的区别在于,由于它是在单独的包中,无法改变 http.Request 类型, 所以你需要用 mux.PathValue(request, "name") 来获取路径值,而非 request.PathValue("name")。

      我在 我的 go-routing 仓库 中添加了一个 PR,这个 PR 提供了我自己的 widget API 的一种实现,使用 muxpatterns。这个版本与 chi 版本 非常相似 —— 清晰且易读:

      复制

      r.HandleFunc("GET /{$}", home)

      r.HandleFunc("GET /contact", contact)

      r.HandleFunc("GET /api/widgets", apiGetWidgets)

      r.HandleFunc("POST /api/widgets", apiCreateWidget)

      r.HandleFunc("POST /api/widgets/{slug}", apiUpdateWidget)

      r.HandleFunc("POST /api/widgets/{slug}/parts", apiCreateWidgetPart)

      r.HandleFunc("POST /api/widgets/{slug}/parts/{id}/update", apiUpdateWidgetPart)

      r.HandleFunc("POST /api/widgets/{slug}/parts/{id}/delete", apiDeleteWidgetPart)

      r.HandleFunc("GET /{slug}", widgetGet)

      r.HandleFunc("GET /{slug}/admin", widgetAdmin)

      r.HandleFunc("POST /{slug}/image", widgetImage)

      当我首次测试这个参考实现时,我发现了一些小问题,现已得到修复。

      结论

      尽管我对于扩展现有的 Handle 和 HandleFunc 方法有一些保留,但我对这个提案的考虑感到欣慰。鉴于 Jonathan 在提案中的谨慎处理、他在 log/slog 上的良好表现以及社区的积极反馈,此提案被接受的可能性很高。

      如果这个功能能进入标准库,那将非常棒 —— 我开发的几乎所有网站和 REST 风格的 API 都将用到这个功能。Go 的标准库已经非常强大,但加入这个功能将进一步减少对第三方路由器的依赖。

      如果这个功能能够集成在 2024 年 2 月发布的 Go 1.22 中,我并不会感到惊讶。让我们拭目以待!

      译者介绍

      刘汪洋,51CTO社区编辑,昵称:明明如月,一个拥有 5 年开发经验的某大厂高级 Java 工程师,拥有多个主流技术博客平台博客专家称号。

      文章内容仅供阅读,不构成投资建议,请谨慎对待。投资者据此操作,风险自担。

    即时

    新闻

    明火炊具市场:三季度健康属性贯穿全类目

    奥维云网(AVC)推总数据显示,2024年1-9月明火炊具线上零售额94.2亿元,同比增加3.1%,其中抖音渠道表现优异,同比有14%的涨幅,传统电商略有下滑,同比降低2.3%。

    企业IT

    重庆创新公积金应用,“区块链+政务服务”显成效

    “以前都要去窗口办,一套流程下来都要半个月了,现在方便多了!”打开“重庆公积金”微信小程序,按照提示流程提交相关材料,仅几秒钟,重庆市民曾某的账户就打进了21600元。

    3C消费

    华硕ProArt创艺27 Pro PA279CRV显示器,高能实力,创

    华硕ProArt创艺27 Pro PA279CRV显示器,凭借其优秀的性能配置和精准的色彩呈现能力,为您的创作工作带来实质性的帮助,双十一期间低至2799元,性价比很高,简直是创作者们的首选。

    研究

    中国信通院罗松:深度解读《工业互联网标识解析体系

    9月14日,2024全球工业互联网大会——工业互联网标识解析专题论坛在沈阳成功举办。