Go1.22 有一个比较重要的新特性,那就是基于提案《net/http: enhanced ServeMux routing》,增强了 http.ServerMux 的路由匹配能力,非常值得大家学习和关注。
本次的新特性主要是新增了 HTTP 方法和路径变量的支持。
快速学习
在 Go 中,可以认为几乎所有的路由相关的库都会基于 net/http 或是兼容其 interface。否则容易脱离一个标准路线。
本文的主角 ServeMux,函数签名如下:
type ServeMux
func NewServeMux() *ServeMux
func (mux *ServeMux) Handle(pattern string, handler Handler)
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request))
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string)
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request)
最简单的 Demo 如下:
type apiHandler struct{}
func (apiHandler) ServeHTTP(http.ResponseWriter, *http.Request) {}
func main() {
mux := http.NewServeMux()
mux.Handle("/api/", apiHandler{})
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
...
fmt.Fprintf(w, "你好,靓仔!)
})
}
用法非常的简单和标准,就是路径和实现方法。
新提案
缘由
这个提案居然没有显著的说明没有这个的缺陷或问题。说明大家在潜意识里已经是比较认可的,不需要额外解释。
我大胆猜测原因应该是:net/http.ServeMux
所提供的路由功能过于的基础了。
因此路由使用上,很多人都不优先推荐使用 net/http,而是更推荐 gorilla/mux 等知名开源库作为基础库,提高实现的效率和产能。
本次新提案将会增加:模式匹配、路径变量的支持。
Go1.22 路由增强
匹配方法
模式匹配将支持以 HTTP 方法开头,后跟空格,如 GET /eddycjy
或 GET eddycjy.com/
中。带有方法的模式仅用于匹配具有该方法的请求。
对照到代码中,也就是 Go1.22 起,http.ServeMux 可以这么写:
mux.HandleFunc("POST /eddycjy/create", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "脑子进煎鱼了!")
})
mux.HandleFunc("GET /eddycjy/update", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "煎鱼进脑子了...")
})
...
通配符
模式匹配将支持 {name}
或 {name...}
,例如:/b/{bucket}/o/{objectname...}
。
该名称必须是有效的 Go 标识符和符合完整路径元素的标准。它们前面必须有斜杠,后面必须有斜杠或字符串末尾。例如:/b_{bucket}
不是有效的通配模式。
Go1.22 起,http.ServeMux 可以这么写:
mux.HandleFunc("/eddycjy/{id}", func(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
fmt.Fprintf(w, "id 值为 %s", id)
})
mux.HandleFunc("/eddycjy/{path...}", func(w http.ResponseWriter, r *http.Request) {
path := r.PathValue("path")
fmt.Fprintf(w, "path 值为 %s", path)
})
...
优先级
单一的优先规则:
- 如果两个模式重叠(有一些共同的请求),那么更具体的模式优先。
- 如果 P1 符合 P2 请求的一个(严格)子集,也就是说:如果 P2 符合 P1 的所有请求及更多请求,那么 P1 就比 P2 更具体。
- 如果两者都不更具体,那么模式就会发生冲突。
- 这条规则有一个例外:如果两个模式发生冲突,而其中一个有 HOST ,另一个没有,那么有 HOST 的模式优先。
以下是图示参考:
具体的例子:
example.com/
比/
更具体,因为第一个仅匹配主机example.com
的请求,而第二个匹配任何请求。GET /
比/
更具体,因为第一个仅匹配 GET 和 HEAD 请求,而第二个匹配任何请求。/b/{bucket}/o/default
比/b/{bucket}/o/{noun}
更具体,因为第一个仅匹配第四个元素是文字 “default” 的路径,而在第二个中,第四个元素可以是任何内容。
总结
今天我们分享了 Go1.22 对于 net/http 标注库中的 ServerMux 路由相关功能的增强。
Go 还是有在吸收第三方开源库的一些不错的地方的,就是慢了一些。这都 10 多年了…