在Go语言原生的HTTP路由中大家是否注意到过功能不是很完善,比如需要硬编码区分GET还是POST请求,在Go 1.22中对Go的HTTP路由做了一定程度的增强,这都要源于一个提案:net/http: enhanced ServeMux routing(https://github.com/golang/go/issues/61410)
在这个提案中提到了Go语言的路由应该变为:
代码语言:go复制GET example.com/
或者:
代码语言:go复制GET /foo
再或者:
代码语言:go复制GET example.com/foo
可以抽象为以下语义分配路由:
代码语言:shell复制[METHOD] [HOST]/[PATH]
解释:
- METHOD:要为其执行分配的处理程序的请求方式,如GET、PUT、POST等。
- HOST:要使用处理程序的主机名。
- PATH:要将处理程序映射到的 Request 路径。
提案中还提到,其中一种模式比另一种模式更具体:
example.com/
比/
更具体,因为第一个仅匹配与主机example.com
的请求,而第二个匹配任何请求。GET /
比/
更具体,因为第一个只匹配 GET 和 HEAD 请求,而第二个匹配任何请求。HEAD /
比GET /
更具体,因为第一个只匹配 HEAD 请求,而第二个同时匹配 GET 和 HEAD 请求。/b/{bucket}/o/default
比/b/{bucket}/o/{noun}
更具体,因为第一个元素仅匹配第四个元素是文本“默认”的路径,而在第二个元素中,第四个元素可以是任何元素。
下面我们用代码对比下:
在1.22版本前的Go语言原生HTTP路由中,如果要区分GET和POST请求,我们需要这样:
代码语言:go复制func OriginalHttpService() {
mux := http.NewServeMux()
mux.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
_, _ = w.Write([]byte("Hello, This is GET method. [Go]"))
case http.MethodPost:
_, _ = w.Write([]byte("Hello, This is POST method. [Go]"))
}
})
_ = http.ListenAndServe(":8801", mux)
}
如果不使用原生的HTTP包,我们可以用Gin框架,它就变成了这样:
代码语言:go复制func GinHttpService() {
engine := gin.Default()
engine.GET("/hello", func(c *gin.Context) {
c.JSON(http.StatusOK, "Hello, This is GET method. [Gin]")
})
engine.POST("/hello", func(c *gin.Context) {
c.JSON(http.StatusOK, "Hello, This is POST method. [Gin]")
})
_ = engine.Run(":8802")
}
如果到了Go1.22,我们就可以不使用框架实现这个功能:
代码语言:go复制func Go22NewHttpService() {
mux := http.NewServeMux()
mux.HandleFunc("GET /hello", func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte("Hello, This is GET method. [Go 1.22]"))
})
mux.HandleFunc("POST /hello", func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte("Hello, This is POST method. [Go 1.22]"))
})
_ = http.ListenAndServe(":8803", mux)
}
是不是觉得优雅了许多~
除此之外,新版本还支持了Restful风格的API,比如:
代码语言:go复制func Go22RestfulApi() {
mux := http.NewServeMux()
mux.HandleFunc("GET /hello/{name}", func(w http.ResponseWriter, r *http.Request) {
name := r.PathValue("name")
resp := fmt.Sprintf("Hello %s, This is GET method.", name)
_, _ = w.Write([]byte(resp))
})
_ = http.ListenAndServe(":8803", mux)
}
在Path重叠的情况下,Go官方还提供了优先级的解释,比如 /posts/latest
和 /posts/{id}
在同一个HTTP服务中,会以更具体的作为优先,显然 /posts/latest
比/posts/{id}
更具体,可以参考:https://go.dev/blog/routing-enhancements会有更详细的说明
我正在参与2024腾讯技术创作特训营最新征文,快来和我瓜分大奖!