Go http.ServeMux中的模式匹配

Go 1.22中一个很大的变化是http.ServeMux中的模式匹配,先前这个功能是很多第三方的web框架或者router库实现的。

我们很有必要好好研究它,将来在实现HTTP API的时候可能优先使用它。

http.ServeMux是一个HTTP请求多路复用器,它将每个传入请求的URL与已注册的模式列表进行匹配,并调用与最接近URL匹配的模式对应的处理程序。

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mux.HandleFunc("POST /items/create", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "POST item created")
})
mux.HandleFunc("/items/create", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "item created")
})
mux.HandleFunc("/items/{id}", func(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
fmt.Fprintf(w, "Item ID = %s", id)
})
mux.HandleFunc("/files/{path...}", func(w http.ResponseWriter, r *http.Request) {
path := r.PathValue("path")
fmt.Fprintf(w, "File path = %s", path)
})

和原来的HandleFunc相比,第一个参数貌似有了不一样的变化,除了正常的path之外,还有HTTP POST Method,还有{id}{path...}这样的变量,这就是Go 1.22中新增加的模式匹配。

通过PathValue可以获取路径中的通配符匹配的值。

模式匹配

模式匹配用在注册handler的时候,如下:

1
2
func Handle(pattern string, handler Handler)
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))

其中pattern模式的格式是这样子的:[METHOD ][HOST]/[PATH], 中括号代表这一项可以省略。
因此POST /items/createPOST rpcx.io/items/createGET /items/123/items/123都是合法的模式。

如果不设置HTTP Method如POST,那么默认是匹配所有的HTTP Method。注意HTTP Method 之后有一个空格。
模式匹配GET会匹配GETHEAD,除此之外,其他的HTTP Method都是精确匹配。

如果未设置HOST,那么默认是匹配所有的HOST。否则,HOST必须完全匹配。

通配符

请求的路径中可以包含通配符,如"/b/{bucket}/o/{objectname...}"。配符名称必须是有效的Go标识符。通配符必须是完整的路径段,例如/b_{bucket}就不是一个合法的通配符。

  • /items/{id}: 正常情况下一个通配符只匹配一个路径段,比如匹配/items/123,但是不匹配/items/123/456
  • /items/{apth...}: 但是如果通配符后面跟着...,那么它就会匹配多个路径段,比如/items/123/items/123/456都会匹配这个模式。
  • /items/{$}: 以/结尾的模式会匹配所有以它为前缀的路径,比如/items//items/123/items/123/456都会匹配这个模式。如果以/{$}为后缀,那么表示严格匹配路径,不会匹配带后缀的路径,比如这个例子只会匹配/items/,不会匹配/items/123/items/123/456

在匹配过程中,模式路径和传入请求路径都会逐段解码。因此,例如,路径 /a%2Fb/100%25 被视为具有两个段,a/b100%。模式 /a%2fb/ 与之匹配,但模式 /a/b/ 则不匹配。

优先级

如果两个模式都可以匹配同一个路径咋办呢?比如/items/{id}/items/{path...}都可以匹配/items/123,那么谁优先呢?

  • 最具体的模式具有优先权。比如/items/{id}/items/更具体。
  • 如果两者都不更具体,则模式冲突。
    • 在冲突的情况下,具有主机的模式具有优先权 rpcx.io/items/{id}/items/{id}优先权更高。
    • 如果两者都没有HOST,则模式冲突,panic。 items/{id}/items/{index}都没有HOST,所以会panic。

后缀/的转发

/images/ 会导致ServeMux/images重定向到/images/除非你注册了/images的handler。

请求清理

ServeMux 还负责清理URL请求路径和Host标头,去除端口号,并将包含 ... 段或重复斜杠的任何请求重定向到等效、更清晰的URL。