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

这是官方博文,原文地址:https://go.dev/blog/routing-enhancements。

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

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

增强功能

新的路由功能只影响传递给两个net/http.ServeMux方法HandleHandleFunc的模式字符串,以及相应的顶级函数http.Handlehttp.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文档。

转载:站长 polarisxu

版权声明:
作者:89391311
链接:https://www.csev.cn/it/20240402147.html/
来源:测试分享
版权声明:本文欢迎任何形式转载,转载时完整保留本声明信息(包含原文链接、原文出处、原文作者、版权声明)即可。本文后续所有修改都会第一时间在原始地址更新。
THE END
根据我国《计算机软件保护条例》第十七条规定:“为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。本站资源仅供个人学习交流,请于下载后 24 小时内删除,不允许用于商业用途,否则法律问题自行承担。
分享
二维码
打赏
< <上一篇
下一篇>>
文章目录
关闭
目 录