目录
入围的router
http.ServeMux
julienschmidt/httprouter
go-chi/chi
gorilla/mux
荣誉提名 bmizerany/pat 和 matryer/way
流程图
其他router
平时你是不是也纠结该用哪个router呢?土拨鼠今天带来一篇router比较的文章,土拨鼠翻译这篇文章也收获了很多(了解router的特性、优秀的代码欣赏等),同时希望能给大家在选择router的时候提供一些参考意见。本文主要翻译自https://www.alexedwards.net/blog/which-go-router-should-i-use。由于土拨鼠翻译水平有限,不足之处烦请指出。另外作者也出书了。书名《Let’s Go》[1] 和 《Let’s Go Further》[2]。主要介绍了如何去使用 Go 构建专业的生产级 web 应用程序和 api。感兴趣的同学可以看看。
本文外链较多,想要更好体验的话可以阅读原文跳转到Go招聘网站上进行阅读。
当你开始使用 Go 构建 web 应用程序时,你可能会问的第一个问题就是“我应该使用哪个router?”.
这也不是一个容易回答的问题。可能有超过100种不同的router[3],它们都有不同的 api、特性和行为。因此,在这篇博客文章中,评估了30个受欢迎的案例,并评估出了一个最佳选项的名单列表,以及一个流程图,可以用来帮助指导你作出选择。
在我们开始之前,有几个关于术语的注意事项:
所说的支持基于方法的路由是指可以很容易地根据请求方法(
GET
、POST
等)将不同的 HTTP 请求分发给不同的handler。通过支持 URL 路径中的变量,这里的意思是router可以很容易地声明像
/movies/{ id }
这样的路由,其中{ id }
是 URL 路径中的动态变量值。通过支持正则regexp 路由模式,意思是router可以很容易地声明像
/movies/{[ a-z-] + }
这样的路由,其中[ a-z-] +
是 URL 路径中必需的 regexp 匹配。通过支持基于主机的路由,意思是,router可以很容易地根据 URL 主机(如
www.example. com
)而不仅仅是 URL 路径向不同的handler发送 HTTP 请求。通过支持自定义路由规则,意思是router可以很容易地为路由请求添加自定义规则(例如根据 IP 地址或header中
Authorization
的值路由到不同的handler)。这里所说的冲突路由指的是当注册两个(或更多)路由模式时,它们可能匹配相同的请求 URL 路径。例如,如果您注册了路由
/blog/{ slug }
和/blog/new
,那么带有路径/blog/new
的 HTTP 请求将会匹配这两个路由。
注意: 从软件工程的角度来看,路由冲突并不是件好事。它可能就是 bug 和混乱的来源,我们应该在应用程序中尽量避免它们。
入围的router
有四种不同的router进入了入围名单。它们是 `http.ServeMux`[4], `julienschmidt/httprouter`[5], `go-chi/chi`[6] 和 `gorilla/mux`[7]. 。这四个router都经过了充分的测试,文档也写得很友好,并且到现在还在积极地维护中。它们(大多数)有稳定的 api,并且与 http.Handler
, http.HandlerFunc
兼容。标准库的中间件模式[8]。
在性能方面,所有四个router对于(几乎)每个应用程序来说都足够快,建议你可以根据需要的特定功能而不是性能来选择它们。我个人曾不同时间在生产应用程序中使用过这四个router,并且对它们感到非常满意。
http.ServeMux
我首先要说的是,如果你可以使用 `http.ServeMux`[9],就近可能应该使用它。
作为 Go 标准库的一部分,它经过了严格的实战测试并有详细的文档记录。使用它意味着不需要导入任何第三方依赖项,而且大多数其他 Go 开发人员也会熟悉它的工作原理。Go 1 兼容性承诺还意味着应该能够http.ServeMux
长期依靠相同的方式工作。所有这些在应用程序维护方面都是很大的积极因素。
与其他大多数router不同,它还支持基于主机的路由,传入请求的 url 会自动清理,并且它匹配路由的方式也很聪明: 较长的路由模式总是优先于较短的路由。这是一个很好的副作用,您可以以任何顺序注册,并且它不会改变应用程序的行为。
两个主要限制http.ServeMux
的是它不支持基于方法的路由或 URL 路径中的变量。但是缺乏对基于方法的路由的支持并不总是避免使用它的好理由——可以使用下面这样的代码解决:
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", index)
err := http.ListenAndServe(":3000", mux)
log.Fatal(err)
}
func index(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
// Common code for all requests can go here...
switch r.Method {
case http.MethodGet:
// Handle the GET request...
case http.MethodPost:
// Handle the POST request...
case http.MethodOptions:
w.Header().Set("Allow", "GET, POST, OPTIONS")
w.WriteHeader(http.StatusNoContent)
default:
w.Header().Set("Allow", "GET, POST, OPTIONS")
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
}
在上面几行代码中就可以实现了基于方法的路由,以及定制的404和405响应和对 OPTIONS
请求的支持。
随着时间的推移,我逐渐意识到这http.ServeMux
有很多优秀的地方,而且在很多情况下已经足够了。事实上,我唯一建议不要使用它的时候是需要支持 URL 路径中的变量或自定义路由规则。在这些情况下,尝试使用http.ServeMux
可能会有点麻烦,我认为选择第三方router通常会更好。
julienschmidt/httprouter
我认为这`julienschmidt/httprouter`[10]的行为和对 HTTP 规范的合规性方面,是任何第三方router最接近“完美”的。它支持基于方法的路由和动态 URL。它还自动处理OPTIONS
请求并正确地发送405
响应码,而且允许为404
和 405
响应设置自定义的handler。
它不支持基于主机的路由、自定义路由规则或 regexp 路由模式。需要注意的是,httprouter
也不允许有冲突的路由,比如/post/create
和 /post/:id
。这在客观上是一件好事,因为它有助于避免 bug,但如果您需要使用冲突的路由(例如与现有系统使用的路由保持一致) ,则可能会出现问题。
httprouter
的一个缺点是,API和文档有点混乱。这个包是在Go 1.7中引入请求上下文之前就已发布,很多当前的API仍然存在,以支持这些旧版本的Go。现在,你可以使用常规的http.Handler
和http.HandlerFunc
签名来写你的handler,你所需要的是要注册router.Handler()
和router.HandlerFunc()
方法。例如:
func main() {
router := httprouter.New()
router.HandlerFunc("GET", "/", indexGet)
router.HandlerFunc("POST", "/", indexPost)
err := http.ListenAndServe(":3000", mux)
log.Fatal(err)
}
func indexGet(w http.ResponseWriter, r *http.Request) {
// Handle the GET request...
}
func indexPost(w http.ResponseWriter, r *http.Request) {
// Handle the POST request...
}
go-chi/chi
`go-chi/chi`[11] 包支持基于方法的路由、 URL 路径中的变量和 regexp 路由模式。像 · 一样,它也允许你为404和405个响应设置自定义handler。
但我最喜欢的是,在chi
中可以创建使用特定中间件的路由 "组",比如下面的代码片段。这对于有很多中间件和路由需要管理的大型应用来说非常有用。
r := chi.NewRouter()
// Middleware used on all routes
r.Use(exampleMiddlewareOne)
r.Use(exampleMiddlewareTwo)
r.Get("/one", exampleHandlerOne)
r.Group(func(r chi.Router) {
// Middleware only used in this route group
r.Use(exampleMiddlewareThree)
r.Get("/two", exampleHandlerTwo)
})
值得注意的是,chi
允许冲突的路由,路由是按照声明的顺序进行匹配的。
chi
的两个缺点是它不能自动处理OPTIONS请求,而且它不能在405响应中设置Allow
header。如果你正在建立一个网络应用程序或私人API,那么这些东西可能不是一个大问题--但如果你正在建立一个公共API,这就是需要注意的事情了。和httprouter
一样,它也不支持基于主机的路由.
值得注意的是,在过去的6年里,chi
已经有了5个主要的版本增量,其中大部分都包含了破坏性的变化。历史并不一定能预测未来,但确实表明,对于 chi
来说,向后兼容性和不进行性更改并不像这个短名单上的其他一些router那么重要。相比之下,httprouter
和 gorilla/mux
在这段时间里并没有做出任何突破性的改变。
值得注意的是,在过去的6年中,chi
已经有5个主要的版本增量--其中大部分都包括重大变化。历史不一定是未来的预测,但确实表明,与这个入围名单上的其他一些路由器相比,向后兼容和不做破坏性的修改对chi
来说不是那么重要。相比之下,httprouter
和gorilla/mux
在这段时间内没有做任何破坏性的改动。
gorilla/mux
gorilla/mux
包可能是最著名的Go router,这是有原因的。它包括了各种功能,包括支持基于方法的路由、动态URL、regexp路由模式和基于主机的路由。重要的是,它是这个入围名单上唯一支持自定义路由规则和路由 "反转 "的router(就像你在Django、Rails或Laravel中看到的那样)。它还允许你为404和405响应设置自定义handler。
它的缺点基本上和 chi
一样ーー它不会像 httprouter
那样自动处理 OPTIONS
请求,也不会在405响应中包含 Allow
header。同样,和 chi
一样,它也允许相互冲突的路由,会按声明的顺序进行匹配。
鉴于chi
和gorilla/mux
的缺点很相似,在两者之间你可以直接选择:如果你需要支持自定义路由规则、基于主机的路由或路由 "反转",选择gorilla/mux
。如果你不需要这些 "高级 "功能,那么最好选择chi
,因为它有很好的管理中间件的功能,特别是如果你正在构建一个大型应用。
荣誉提名 bmizerany/pat 和 matryer/way
我认为值得一提的另外两个router是`bmizerany/pat`[12] 和 `matryer/way`[13]。我对这两个都情有独钟,因为它们都是故意简单化的。它们拥有很小的 api 和非常清晰易懂的代码,这使得我们可以很容易准确地掌握router在幕后是如何工作的。尤其是matryer/way
背后的代码,非常值得一读。
虽然它们的功能不如入围名单中的其他router,但我认为它们的简单性使它们很适合用于教程(尤其是针对入门gopher的教程),如果你想建立自己的自定义router,可以把它们作为一个起点/灵感。
流程图
考虑到各种利弊和支持的功能,这个流程图应该可以帮助你在四个入围的router之间做出选择。
其他router
下面列出了评估的其他router,以及一个简短的说明,解释了为什么它们没有进入名单中。
注意: 使用这个问题“代码库是否包含 go.mod 文件?”作为一个代理来测量一个代码库当前是否在维护。这似乎是合理的——如果维护者仍然参与Go生态并关注代码,他们应该会在过去几年的某个时候使用modules来更新代码库。
Repository | Notes |
---|---|
celrenheit/lion[14] | 目前尚未维护 |
claygod/Bxog[15] | 目前尚未维护 |
clevergo/clevergo[16] | 使用自定义handler签名(不是 http.Handler 或 http.HandlerFunc)。 |
dimfeld/httptreemux[17] | 不完全支持 http.Handler。需要中间件来设置自定义 404/405 handler。 |
donutloop/mux[18] | 目前尚未维护 |
gernest/alien[19] | 目前尚未维护 |
go-ozzo/ozzo-routing[20] | 使用自定义handler签名(不是 http.Handler 或 http.HandlerFunc)。 |
go-playground/lars[21] | 目前尚未维护 |
go-zoo/bone[22] | 很好,但与 chi 有类似的用例(提供的更多)。但是测试不够完整的。 |
go101/tinyrouter[23] | 详细的路由说明。不会自动发送 405 响应。 |
gocraft/web[24] | 目前尚未维护 |
goji/goji[25] | 稍微有点不寻常但具备灵活的 API,支持自定义匹配程序。需要中间件来设置自定义 404/405 handler。也很好,但我认为 gorilla/mux 提供了类似的功能并且更易于使用。 |
goroute/route[26] | 使用自定义handler签名(不是 http.Handler 或 http.HandlerFunc)。 |
gowww/router[27] | 很好,但是有类似 chi 的用例(提供更多)。没有办法设置自定义405handler。 |
GuilhermeCaruso/bellt[28] | 无法设置自定义404或405 handlers. |
husobee/vestigo[29] | 目前尚未维护 只支持http.HandlerFunc. |
naoina/denco[30] | 目前尚未维护 |
nbari/violetear[31] | 很好,但是和 chi 有类似的用例(它提供更多)。wrap http.ResponseWriter 用自己的定制类型,这在某些情况下可能会出现问题。 |
nbio/hitch[32] | 缺乏文档 |
nissy/bon[33] | 目前尚未维护 |
razonyang/fastrouter[34] | 目前尚未维护 |
rs/xmux[35] | 目前尚未维护 使用自定义handler签名(不是 http.Handler 或 http.HandlerFunc)。 |
takama/router[36] | 目前尚未维护 |
vardius/gorouter[37] | 很好,但是和 chi 有类似的用例(它提供更多)。5年的4个主要版本表明这个 API 可能并不是很可靠。 |
VividCortex/siesta[38] | 很好,但是有类似 chi 的用例(提供更多)。没有办法设置自定义405handler。 |
xujiajun/gorouter[39] | 目前尚未维护 |
参考资料
[1]
《Let’s Go》: https://lets-go.alexedwards.net/
[2]《Let’s Go Further》: https://lets-go-further.alexedwards.net/
[3]100种不同的router: https://github.com/search?l=Go&p=5&q=http+router&type=Repositories
[4]http.ServeMux
: https://pkg.go.dev/net/http#ServeMux
julienschmidt/httprouter
: https://github.com/julienschmidt/httprouter
go-chi/chi
: https://github.com/go-chi/chi
gorilla/mux
: https://github.com/gorilla/mux
标准库的中间件模式: https://www.alexedwards.net/blog/making-and-using-middleware
[9]http.ServeMux
: https://pkg.go.dev/net/http#ServeMux
julienschmidt/httprouter
: https://github.com/julienschmidt/httprouter
go-chi/chi
: https://github.com/go-chi/chi
bmizerany/pat
: https://github.com/bmizerany/pat
matryer/way
: https://github.com/matryer/way
celrenheit/lion: https://github.com/celrenheit/lion
[15]claygod/Bxog: https://github.com/claygod/Bxog
[16]clevergo/clevergo: https://github.com/clevergo/clevergo
[17]dimfeld/httptreemux: https://github.com/dimfeld/httptreemux
[18]donutloop/mux: https://github.com/donutloop/mux
[19]gernest/alien: https://github.com/gernest/alien
[20]go-ozzo/ozzo-routing: https://github.com/go-ozzo/ozzo-routing
[21]go-playground/lars: https://github.com/go-playground/lars
[22]go-zoo/bone: https://github.com/go-zoo/bone
[23]go101/tinyrouter: https://github.com/go101/tinyrouter
[24]gocraft/web: https://github.com/gocraft/web
[25]goji/goji: https://github.com/goji/goji
[26]goroute/route: https://github.com/goroute/route
[27]gowww/router: https://github.com/gowww/router
[28]GuilhermeCaruso/bellt: https://github.com/GuilhermeCaruso/bellt
[29]husobee/vestigo: https://github.com/husobee/vestigo
[30]naoina/denco: https://github.com/naoina/denco
[31]nbari/violetear: https://github.com/nbari/violetear
[32]nbio/hitch: https://github.com/nbio/hitch
[33]nissy/bon: https://github.com/nissy/bon
[34]razonyang/fastrouter: https://github.com/razonyang/fastrouter
[35]rs/xmux: https://github.com/rs/xmux
[36]takama/router: https://github.com/takama/router
[37]vardius/gorouter: https://github.com/vardius/gorouter
[38]VividCortex/siesta: https://github.com/VividCortex/siesta
[39]xujiajun/gorouter: https://github.com/xujiajun/gorouter
欢迎关注Go招聘公众号,获取Go专题、大厂内推、面经、简历、股文等相关资料可回复和点击导航查阅。