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.Handle("/posts/", handlePost)
尾部的斜杠将所有以
/posts/
开始的请求路由到
handlePost
函数,该函数将需要检查HTTP方法是否为GET,提取标识符,并取回帖子。由于方法检查并非严格必要以满足请求,忽略它将是一个自然的错误。这将意味着像
DELETE
/
posts
/
234
这样的请求将取回帖子,这至少是令人惊讶的。而在Go 1.22中,现有代码将继续工作,或者你可以改写成这样:
http.Handle("GET /posts/{id}", handlePost2)
这个模式匹配路径开始以
/posts/
的请求并且有两个段的GET请求将匹配到这个模式(特别是,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一直允许重叠,并且有选择更长的模式的优先级规则,不论注册顺序。保证顺序不依赖很重要并且必须向后兼容,但我们需要一个比“最长的赢”更好的规则。这个规则会选择
/post/
latest
而不是
/post/
{
id
}
,但却会选择
/posts/
{
id
}/{
category
}
而不是
/posts/
{
id