01
介绍
在 Go 1.22 中,标准库 net/http
的 `ServeMux` 路由模式增强[1],可以区分 HTTP 请求方法和支持通配符。
ServeMux
是一个 HTTP 请求多路复用器。它将每个传入请求的 URL 与已注册路由模式列表进行匹配,并调用与 URL 最匹配的路由模式的处理器。
本文我们介绍路由模式增强的多路复用器 ServeMux
的使用方式。
02
路由模式
路由模式可以匹配请求方法、请求 HOST 和请求路径,路由模式的格式如下:
[METHOD ][HOST]/[PATH]
三个部分都是可选的,如果路由模式中存在 METHOD
,它的后面必须有一个空格。
限定 HTTP 请求方法的路由模式
示例代码:
func main() {
// 使用 GET 方法注册处理器函数
mux := http.NewServeMux()
mux.HandleFunc("GET /goods/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "iPhone")
})
// 创建 HTTP 服务器
server := http.Server{
Handler: mux,
}
log.Fatal(server.ListenAndServe())
}
阅读上面这段代码,我们可以发现路由模式参数的字符串中包含 GET
HTTP 请求方法。
GET /goods/
匹配请求路径以 /goods/
开头的 GET
请求。
没有请求方法的路由模式与每个请求方法匹配。具有 GET
方法的请求模式同时匹配 GET
和 HEAD
请求。
需要注意的是,其它请求方法必须完全匹配。
限定请求 HOST 的路由模式
没有 HOST 的路由模式,与任意 HOST 上的 URL 匹配。
具有 HOST 的路由模式,仅与该 HOST 上的 URL 匹配。
比如 example.com/
匹配对 HOST example.com
的任何请求。
example.com/{$}
匹配对 HOST example.com
和路径 /
的请求,即 example.com/
。
通配符的路由模式
请求路径可以包含格式为 {NAME}
或 {NAME...}
的通配符段。例如,/b/{bucket}/o/{objectname...}
。
通配符名称必须是有效的 Go 标识符。通配符必须是完整路径段:它们前面必须有一个斜杠,后面必须有一个斜杠或字符串的末尾。例如,/{bucket}/
或 /{bucket}/name
是有效的路由模式,而 /b_{bucket}
不是有效的路由模式。
通常,通配符仅匹配单个路径段,以请求 URL 中的下一个文字斜杠 /
(而不是 %2F
)结尾。但是,如果存在 ...
,例如 {NAME...},则通配符与 URL 路径的其余部分(包括斜杠)匹配。
因此,对于存在 ...
的通配符,它出现在除路由模式末尾以外的任何位置,都不是有效的路由模式,例如 /b/{bucket}/o/{objectname...}/name
不是有效的路由模式。
可以通过使用 Request.PathValue(bucket)
调用通配符的名称,来获取通配符的匹配项。
需要注意的是,除了新增 PathValue
,还新增了 SetPathValue
。
路径中的尾部斜杠,充当匿名的通配符 ...
。
特殊通配符 {} 仅与 URL 的末尾匹配。例如,路由模式 /{} 仅匹配路径 /,而路由模式 / 匹配任意路径。
为了进行匹配,路由模式的路径和传入请求路径都是逐段未转义的。比如路径 /a%2Fb/100%25
被视为具有两个路径段,a/b
和 100%
。路由模式 /a%2fb/
匹配它,但路由模式 /a/b/
不匹配。
03
优先级
如果两个或多个路由模式与一个请求匹配,则以最具体的路由模式为准。
如果 P1 与 P2 请求的严格子集匹配,也就是如果 P2 与 P1 的所有请求匹配,甚至更多,则路由模式 P1 比 P2 更具体。如果两者都不是更具体的,则路由模式会冲突。
对于向后兼容性,此规则有一个例外:如果两个路由模式会发生冲突,并且一个路由模式具有 HOST
,而另一个路由模式没有,则具有 HOST
的路由模式优先级更高。
如果传递了 ServeMux.Handle
或 ServeMux.HandleFunc
的路由模式与已注册的另一个路由模式冲突,则这些函数会崩溃。
比如 /images/thumbnails/
比 /images/
更具体,因此两者都可以注册。前者匹配以 /images/thumbnails/
开头的路径,后者将匹配 /images/
子树中的任何其他路径。前者的优先级高于后者。
再比如路由模式 GET /
和 /index.html
:两者都匹配 /index.html
的 GET
请求,但前者匹配所有其他 GET
和 HEAD
请求,而后者匹配使用不同请求方法的任何 /index.html
请求。因为两者都不是更具体的路由模式,所以两者冲突。
04
兼容性
在 Go 1.22 中,ServeMux
的路由模式语法和匹配行为发生了重大变化。若要还原旧行为,请将 GODEBUG 环境变量设置为 httpmuxgo121=1
。此设置在程序启动时读取一次,在程序执行期间更改此设置,不会直接生效。
向后不兼容的更改包括:
/{x}
将仅匹配 1.21 中的该路径,但将匹配 1.22 中的任何单段路径。ServeMux.Handle
和 ServeMux.HandleFunc
程序崩溃。例如,在 1.21 中,路由模式 /{
和 /a{x}
匹配它们自己,但在 1.22 中,它们是无效的,路由模式注册时会引发 panic。%61
与路径 /a
匹配(%61
是 a
的 URL 转义序列),但在 1.21 中,它只会匹配路径 /%2561
(其中 %25
是百分号的转义)。05
总结
本文我们介绍 Go 1.22 对 ServeMux
新增的两个增强功能:
感兴趣的读者朋友们,可以阅读参考资料中的链接地址,了解更多。