可以不需要第三方路由了: Go1.22 的路由增强功能

程序员咋不秃头2024-04-01 18:44:26  125

Go 1.22 为 net/http 路由的模式增加了两个功能:方法匹配和通配符。这些功能允许你将常见的路由表达为模式,而不是 Go 代码。尽管这些功能的解释和使用都很简单,但要为多个匹配请求的模式确定优先模式的正确规则是一个挑战。

作为持续努力的一部分,使 Go 成为构建生产系统的优秀语言,我们进行了这些更改。我们研究了许多第三方 Web 框架,提取了我们认为最常用的功能,并将它们集成到net/http中。然后我们通过在GitHub 讨论和提案问题中与社区合作,验证了我们的选择并改进了设计。将这些功能添加到标准库意味着许多项目少了一个依赖。但对于当前用户或具有高级路由需求的程序,第三方 Web 框架仍然是一个很好的选择。

增强功能

新的路由功能只影响传递给两个net/http.ServeMux方法Handle和HandleFunc的模式字符串,以及相应的顶级函数http.Handle和http.HandleFunc。唯一的 API 更改是在net/http.Request上增加了两个用于处理通配符匹配的新方法。

我们将通过一个假设的博客服务器来说明这些变化,该服务器的每篇文章都有一个整数标识符。像GET /posts/234这样的请求将检索 ID 为 234 的帖子。在 Go 1.22 之前,处理这些请求的代码将以类似这样的行开始:

http.HandleFunc("/posts/", handlePost)

斜杠/路由所有以/posts/开头的请求到handlePost函数,该函数必须检查 HTTP 方法是 GET,提取标识符,并检索帖子。由于方法检查并不是满足请求的严格要求,至少可以说这是一个自然的错误,如果忽略了它,那么像DELETE /posts/234这样的请求将获取帖子,这至少是令人惊讶的。

在 Go 1.22 中,现有代码将继续工作,或者你可以改为这样写:

http.HandleFunc("GET /posts/{id}", handlePost2)

这个模式匹配一个 GET 请求,其路径以“/posts/”开头并有两个段。(作为一个特殊情况,GET 也匹配 HEAD;所有其他方法都是精确匹配。)handlePost2函数不再需要检查方法,并且可以使用Request上的新PathValue方法来提取标识符字符串:

idString := req.PathValue("id")

handlePost2的其余部分将像handlePost一样行为,将字符串标识符转换为整数并获取帖子。

像DELETE /posts/234这样的请求,如果没有注册其他匹配的模式,将失败。根据HTTP 语义,net/http服务器将对这类请求回复一个405 Method Not Allowed错误,并在Allow头中列出可用的方法。

通配符可以匹配整个段,如上面示例中的{id},或者如果它以...结尾,它可以匹配路径的所有剩余段,如模式/files/{pathname...}。

还有最后一点语法。正如我们上面展示的,以斜杠结尾的模式,如/posts/,匹配所有以该字符串开头的路径。要仅匹配带有尾随斜杠的路径,你可以写成/posts/{$}。这将匹配/posts/,但不匹配/posts或/posts/234。

还有一个 API:net/http.Request有一个SetPathValue方法,以便标准库之外的路由器可以通过Request.PathValue提供它们自己的路径解析结果。

优先级

每个 HTTP 路由器都必须处理重叠的模式,如/posts/{id}和/posts/latest。这两个模式都匹配路径“posts/latest”,但最多只能有一个模式为请求服务。哪个模式具有优先权?

一些路由器不允许重叠;其他路由器使用最后注册的模式。Go 一直允许重叠,并选择无论注册顺序如何都选择更长的模式。对我们来说,保持顺序独立性很重要(并且对向后兼容是必要的),但我们需要的是一个比“最长者胜”更好的规则。那个规则会选择/posts/latest而不是/posts/{id},但会选择/posts/{identifier}而不是两者。那看起来是错误的:通配符名称不应该有关系。感觉/posts/latest应该总是赢得这场比赛,因为它匹配单个路径而不是多个。

我们寻找一个好的优先级规则的过程中,考虑了许多模式的属性。例如,我们考虑过更喜欢具有最长字面量(非通配符)前缀的模式。那会选择/posts/latest而不是/posts/{id}。但它不能区分/users/{u}/posts/latest和/users/{u}/posts/{id},而且前者应该优先。

我们最终选择了一个基于模式意义而非外观的规则。每个有效模式都匹配一组请求。例如,/posts/latest匹配路径为/posts/latest的请求,而/posts/{id}匹配任何第一个段是“posts”的两段路径的请求。我们说一个模式比另一个模式更具体,如果它匹配的是请求的严格子集。模式/posts/latest比/posts/{id}更具体,因为后者匹配前者所做的每个请求,以及更多。

优先级规则很简单:最具体的模式获胜。这个规则符合我们的直觉,即posts/latest应该优先于posts/{id},/users/{u}/posts/latest应该优先于/users/{u}/posts/{id}。它对方法也有意义。例如,GET /posts/{id}优先于/posts/{id},因为前者只匹配 GET 和 HEAD 请求,而后者匹配任何方法的请求。

如果两个模式重叠但都不是更具体的,怎么办?例如,/posts/{id}和/{resource}/latest都匹配/posts/latest。没有明显的答案来决定哪个具有优先权,所以我们认为这些模式彼此冲突。注册它们两个(无论顺序如何!)将会导致 panic。

优先级规则对方法和路径的工作方式完全相同,但我们不得不为主机做一个例外,以保持兼容性:如果两个模式在其他方面会冲突,而一个有主机而另一个没有,那么有主机的模式具有优先权。

计算机科学的学生可能记得正则表达式和正则语言的美丽理论。每个正则表达式都挑选出一个正则语言,即表达式匹配的字符串集。有些问题通过谈论语言而不是表达式来提出和回答更容易。我们的优先级规则就是受到这个理论的启发。实际上,每个路由模式都对应一个正则表达式,匹配请求的集合扮演着正则语言的角色。

通过语言而不是表达式来定义优先级使得它容易陈述和理解。但基于潜在无限集的规则有一个缺点:不清楚如何有效地实现它。事实证明,我们可以通过逐段检查模式来确定两个模式是否冲突。大致来说,如果一个模式在另一个模式有通配符的地方有字面量段,那么它更具体;但如果字面量在两个方向上都与通配符对齐,那么模式就冲突了。

当在ServeMux上注册新模式时,它会检查与先前注册的模式的冲突。但检查每对模式将需要两次时间。我们使用一个索引来跳过不能与新模式冲突的模式;在实践中,它工作得很好。无论如何,这个检查发生在模式注册时,通常在服务器启动时。在 Go 1.22 中匹配传入请求的时间与以前版本相比没有太大变化。

兼容性

我们尽了最大努力使新功能与 Go 的旧版本兼容。新模式语法是旧模式的超集,新优先级规则推广了旧规则。但有一些边缘情况。例如,以前的 Go 版本接受带有大括号的模式,并将它们视为字面量,但 Go 1.22 使用大括号作为通配符。GODEBUG 设置httpmuxgo121恢复旧行为。

有关这些路由增强功能的更多详细信息,请参见net/http.ServeMux文档。

转载此文是出于传递更多信息目的。若来源标注错误或侵犯了您的合法权益,请与本站联系,我们将及时更正、删除、谢谢。
https://www.414w.com/read/147163.html
0
随机主题
恒生科技指数日内跌超2%山东青州: 小蜜蜂采蜜忙抹不去的记忆和乡愁 四十余年镜头记录夏收的点点滴滴最新战况: 波克罗夫斯克局势紧张, 泽连斯基谈论哈尔科夫形势!彩!彩!彩!荣耀magic6保时捷pk华为pura70ultra!当“茉莉花”遇上“红莓花” 中俄文化交流创新不断50多万的奥迪SUV降价超22万元, 车长4米9马力313匹值不值人需要多少个偶然才能成为自己! 治愈Sensor Tower: 《Monopoly GO! 》4月蝉联全球手游畅销榜冠军, 《王者荣耀》位列第2名, 米哈游旗下两款手游表现强劲全新宝腾S70 R3赛车亮相, 将重返S1K耐力赛!女子为了帮衬娘家, 婚内转移财产300万, 丈夫得知后诉讼离婚同样面对权臣, 孙亮谨小慎微, 曹髦英勇无畏, 谁更值得称道?三个如花似玉的公主咋就出不了一个王后?辽宁三连冠! 500万大咖怒喷: 这是中国篮球的耻辱和悲哀泽连斯基乌克兰总统泽连斯基五周年之际,极力督促北约武器到位!前央视主持离世, 曾解说中国女排夺冠成名, 与郎平魏秋月等是好友丛明晨凌晨4点发博庆祝夺冠: 兄弟们牛逼 纯纯躺赢中国制造又杀回第一!再次超过美国,成印度第一大贸易伙伴11集之后, 《庆余年2》终于挽回口碑, 6个老戏骨, 被严重低估了金辉控股(09993)上涨50.68%, 报3.3元/股极星CEO: 不再依赖沃尔沃和吉利! 两款新车将用“自家”技术
最新回复(0)