Golang中除了Gorilla/Mux之外还有哪些好用的路由库推荐?
Golang中除了Gorilla/Mux之外还有哪些好用的路由库推荐? 我想学习Go语言中的路由(不使用第三方库),我相信我应该学会用Go语言自己实现,而且我尽可能不想依赖第三方库。不是说gorilla/mux不好,但我真的不喜欢使用第三方库,除非别无选择(比如OpenGL、OpenSSL)。
你见过 https://github.com/quii/learn-go-with-tests 吗?
特别是 HTTP 服务器与 JSON、路由和嵌入部分。这些章节展示了如何使用 net/http 开始构建一个简单的服务器。解析 request.URL.Path 获取变量、方法匹配等等…
我相信作者后续会切换到 gorilla/mux。这个过程可能会很有意思。
作为对比观点,我使用纯 net/http 开发过许多网络服务。Gorilla 路由库确实有其适用场景,当你需要在端点路径中处理大量占位符时会发现它很有用。但从 net/http 开始完全合理,直到你因某些原因觉得使用原生库过于繁琐时,这时你就具备了良好的基础来决策是否切换到更复杂的方案。
嗯,避免使用像 gorilla/mux 这样的路由器并不是一个好主意。该项目非常成熟且维护良好,已在数百个项目中使用。当然,你可以从头开始编写所有代码,但实现所有那些匹配、中间件等功能可能需要很长的过程。
如果你担心意外情况(比如不兼容性,或者更糟的……项目被放弃),你可以使用 vendor 文件夹下载、使用并重新分发(请查看许可证)一个固定版本的 mux 与你的项目一起使用。
@geosoft1 说得很有道理——这主要取决于个人偏好和您关注的重点。如果减少代码行数很重要,那么 Gorilla:mux 是不错的选择。如果降低耦合度和提高灵活性更重要,那么在处理程序中实现路由是个好方法。
在处理程序中实现路由的另一个好处是,它帮助您以清晰的层次结构组织路由,因为您不能随意添加路径——所有路径都需要以逻辑方式贯穿。
我还没有使用这种方法实现身份验证等功能,所以对我来说这仍处于实验阶段。但是,身份验证通常通过用中间件包装处理程序来实现,因此我不确定这两种方法在这方面会有显著差异。
我喜欢这种方法:
https://blog.merovius.de/2017/06/18/how-not-to-use-an-http-router.html
另一个示例请参见:
package api
import (
"bytes"
"fmt"
"io"
"log"
"net/http"
"os"
"github.com/simpleiot/simpleiot/assets/frontend"
"github.com/simpleiot/simpleiot/db"
)
// IndexHandler is used to serve the index page
type IndexHandler struct{}
func (h *IndexHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
fmt.Println("indexHandler")
f := frontend.Asset("/index.html")
此文件已被截断。显示完整内容
让我暂时扮演魔鬼代言人,为了建设性的讨论 😊
我同意在小项目、学习场景或独立服务中使用标准库。但对于其他情况,使用路由器(这里我们讨论的是 gorilla)确实能带来便利,特别是 gorilla 让代码变得非常清晰易读。
之前文章中提到的技术方法虽然从技术角度看很有趣,但我认为从项目管理角度来说并不实用。 例如作者认为框架会带来大量代码,而且难以调试。考虑到可执行文件的大小,增加的代码量其实是很合理的。关于调试,你是在处理器层面进行调试,而不是在路由器层面,我不太明白你期望在路由器中调试什么?
另一个问题是像这个或文章中那个代码的复杂性。我看到这些代码不够清晰,难以阅读和理解,可能也难以扩展,这对于严肃的项目来说并不理想,而且也不符合 Go 语言的简洁范式。添加认证或切换方法可能会导致完全混乱……我建议看看这个小例子来理解我的意思。
在Go语言中,标准库net/http提供了内置的路由功能,无需依赖第三方库如Gorilla/Mux。你可以使用http.ServeMux或自定义路由逻辑来实现。下面我将详细说明如何利用标准库构建路由,并给出示例代码。
使用标准库net/http的路由实现
Go的net/http包包含ServeMux类型,它是一个HTTP请求多路复用器(即路由),允许你注册处理器函数到特定路径。此外,你可以通过自定义逻辑扩展路由功能,例如路径参数解析或中间件支持。
示例1:基本路由使用http.ServeMux
这个示例展示了如何使用http.ServeMux注册简单的路由,并启动HTTP服务器。
package main
import (
"fmt"
"net/http"
)
func main() {
mux := http.NewServeMux()
// 注册路由处理器
mux.HandleFunc("/", homeHandler)
mux.HandleFunc("/about", aboutHandler)
mux.HandleFunc("/user/", userHandler) // 注意:以斜杠结尾,匹配以/user/开头的路径
// 启动服务器
fmt.Println("服务器运行在 http://localhost:8080")
http.ListenAndServe(":8080", mux)
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
fmt.Fprintf(w, "欢迎访问首页!")
}
func aboutHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "这是关于页面。")
}
func userHandler(w http.ResponseWriter, r *http.Request) {
// 简单解析路径:例如 /user/123 提取ID
path := r.URL.Path
if len(path) > len("/user/") {
userID := path[len("/user/"):]
fmt.Fprintf(w, "用户ID: %s", userID)
} else {
http.NotFound(w, r)
}
}
在这个例子中:
- 我们创建了一个新的
ServeMux实例。 - 使用
HandleFunc方法注册了根路径/、/about和以/user/开头的路径的处理器函数。 - 对于
/user/路径,我们手动解析路径参数(如用户ID),而不依赖第三方库。 - 服务器监听8080端口,处理传入的HTTP请求。
示例2:自定义路由逻辑实现路径参数和中间件
如果需要更高级的功能,如路径参数提取或中间件,你可以通过自定义处理器函数实现。以下示例演示了如何解析路径参数(例如RESTful风格的/users/{id})并添加简单的日志中间件。
package main
import (
"fmt"
"net/http"
"strings"
)
// 自定义路由结构,用于存储路由映射
type Router struct {
routes map[string]http.HandlerFunc
}
func NewRouter() *Router {
return &Router{
routes: make(map[string]http.HandlerFunc),
}
}
// 添加路由:支持简单路径匹配,这里使用精确匹配,但可以扩展为通配符
func (r *Router) AddRoute(path string, handler http.HandlerFunc) {
r.routes[path] = handler
}
// 实现http.Handler接口,处理请求
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
path := req.URL.Path
// 检查精确匹配的路由
if handler, exists := r.routes[path]; exists {
// 调用中间件包装的处理器(这里添加简单日志)
logMiddleware(handler).ServeHTTP(w, req)
return
}
// 处理动态路径,例如 /users/123
if strings.HasPrefix(path, "/users/") {
userID := strings.TrimPrefix(path, "/users/")
if userID != "" {
// 调用用户详情处理器,传递用户ID
userDetailHandler(w, req, userID)
return
}
}
// 如果没有匹配的路由,返回404
http.NotFound(w, req)
}
// 中间件:日志记录
func logMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("收到请求: %s %s\n", r.Method, r.URL.Path)
next(w, r)
}
}
// 处理器函数
func homeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "首页内容")
}
func aboutHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "关于我们")
}
func userDetailHandler(w http.ResponseWriter, r *http.Request, userID string) {
fmt.Fprintf(w, "用户详情,ID: %s", userID)
}
func main() {
router := NewRouter()
router.AddRoute("/", homeHandler)
router.AddRoute("/about", aboutHandler)
fmt.Println("服务器运行在 http://localhost:8080")
http.ListenAndServe(":8080", router)
}
在这个例子中:
- 我们定义了一个自定义
Router结构,使用映射来存储路由和处理器函数。 ServeHTTP方法处理所有传入请求,检查路径匹配,并支持动态路径解析(如/users/{id})。- 添加了
logMiddleware作为中间件,用于记录请求日志,展示了如何扩展功能。 - 这种方法避免了第三方依赖,同时提供了灵活的路由控制。
总结
通过Go标准库的net/http,你可以实现强大的路由功能,包括静态路径、动态路径参数和中间件。以上示例展示了从基础到高级的用法,帮助你构建不依赖第三方库的Web应用。如果你需要更复杂的特性(如正则表达式路由),可以进一步扩展自定义逻辑,而无需引入外部包。


