大家好,我是煎鱼。

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)
})
...

优先级

单一的优先规则:

  1. 如果两个模式重叠(有一些共同的请求),那么更具体的模式优先。
  2. 如果 P1 符合 P2 请求的一个(严格)子集,也就是说:如果 P2 符合 P1 的所有请求及更多请求,那么 P1 就比 P2 更具体。
  3. 如果两者都不更具体,那么模式就会发生冲突。
  4. 这条规则有一个例外:如果两个模式发生冲突,而其中一个有 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 多年了...

文章持续更新,可以微信搜【脑子进煎鱼了】阅读,本文 GitHub github.com/eddycjy/blog 已收录,学习 Go 语言可以看 Go 学习地图和路线,欢迎 Star 催更。

推荐阅读


煎鱼
8.4k 声望12.8k 粉丝