如何组织Gorilla Mux的路由?

19

我正在使用Gorilla Mux编写REST API,但我在组织路由时遇到了麻烦。目前所有的路由都像这样在main.go文件中定义:

//main.go
package main

import (
    "NovAPI/routes"
    "fmt"
    "github.com/gorilla/mux"
    "net/http"
)

func main() {

    router := mux.NewRouter().StrictSlash(true)

    router.HandleFunc("/hello", func(res http.ResponseWriter, req *http.Request) {
        fmt.Fprintln(res, "Hello")
    })

    router.HandleFunc("/user", func(res http.ResponseWriter, req *http.Request) {
        fmt.Fprintln(res, "User")
    })

    router.HandleFunc("/route2", func(res http.ResponseWriter, req *http.Request) {
        fmt.Fprintln(res, "Route2")
    })

    router.HandleFunc("/route3", func(res http.ResponseWriter, req *http.Request) {
        fmt.Fprintln(res, "Route3")
    })

    // route declarations continue like this

    http.ListenAndServe(":1128", router)

}

所以我想做的是将这个路由声明拆分成多个文件,我该如何做呢?提前致谢。

4个回答

23
你可以将路由器模块化为不同的包,并独立地挂载到主路由器上。稍微解释一下此问题的内容,你可以采用这种方法,使其更具可扩展性(在某种程度上更易于测试)。/api/router.go
package api

import (
    "net/http"

    "github.com/gorilla/mux"
)

func Router() *mux.Router {
    router := mux.NewRouter()
    router.HandleFunc("/", home)
    return router
}

func home(w http.ResponseWriter, req *http.Request) {
    w.Write([]byte("hello from API"))
}

/主程序.go

package main

import (
    "log"
    "net/http"
    "strings"

    "github.com/...yourPath.../api"
    "github.com/...yourPath.../user"
    "github.com/gorilla/mux"
)

func main() {
    router := mux.NewRouter()

    router.HandleFunc("/", home)
    mount(router, "/api", api.Router())
    mount(router, "/user", user.Router())

    log.Fatal(http.ListenAndServe(":8080", router))
}

func mount(r *mux.Router, path string, handler http.Handler) {
    r.PathPrefix(path).Handler(
        http.StripPrefix(
            strings.TrimSuffix(path, "/"),
            handler,
        ),
    )
}

func home(w http.ResponseWriter, req *http.Request) {
    w.Write([]byte("Home"))
}

13

这个怎么样?

//main.go
package main

import (
    "NovAPI/routes"
    "fmt"
    "github.com/gorilla/mux"
    "net/http"
)

func main() {

    router := mux.NewRouter().StrictSlash(true)

    router.HandleFunc("/hello", HelloHandler)
    router.HandleFunc("/user", UserHandler)
    router.HandleFunc("/route2", Route2Handler)
    router.HandleFunc("/route3", Route3Handler)
    // route declarations continue like this

    http.ListenAndServe(":1128", router)

}

func HelloHandler(res http.ResponseWriter, req *http.Request) {
    fmt.Fprintln(res, "Hello")
}

func UserHandler(res http.ResponseWriter, req *http.Request) {
    fmt.Fprintln(res, "User")
}

func Route2Handler(res http.ResponseWriter, req *http.Request) {
    fmt.Fprintln(res, "Route2")
}

func Route3Handler(res http.ResponseWriter, req *http.Request) {
    fmt.Fprintln(res, "Route3")
}

这样您就可以将处理程序放在其他文件甚至其他包中。

如果您遇到像数据库这样的额外依赖项,甚至可以使用构造函数技巧避免使用全局变量:

//main.go

func main() {
    db := sql.Open(…)

    //...

    router.HandleFunc("/hello", NewHelloHandler(db))

    //...
}

func NewHelloHandler(db *sql.DB) func(http.ResponseWriter, *http.Request) {
    return func(res http.ResponseWriter, req *http.Request) {
        // db is in the local scope, and you can even inject it to test your
        // handler
        fmt.Fprintln(res, "Hello")
    }
}

我为了简单起见这样做了,但我的处理程序实际上是在路由包中定义的,因此我仍然需要将路由从主函数中取出。 - zola
2
我不明白这个意思:无论如何,你仍然需要在某个地方编写你的路由...如果你的“main”函数太长了,也许你可以编写一个“NewRouter”帮助程序来为你初始化它。 - Elwinar
1
另一种解决方案是在您的路由包中拥有一个初始化函数,该函数以路由器作为输入并根据需要添加路由。但我强烈不建议这样做。 - Elwinar

6

我喜欢查看GitHub上的其他项目,以获取如何完成任务的灵感。对于这些情况,我通常首先查看Docker repo。以下是他们的做法:

对于系统的路由,将所有处理程序定义在system_routes.go中,然后在system.go中的NewRouter函数中初始化这些路由。

type systemRouter struct {
    backend Backend
    routes  []router.Route
}

func NewRouter(b Backend) router.Router {
    r := &systemRouter{
        backend: b,
    }

    r.routes = []router.Route{
        local.NewOptionsRoute("/", optionsHandler),
        local.NewGetRoute("/_ping", pingHandler),
        local.NewGetRoute("/events", r.getEvents),
        local.NewGetRoute("/info", r.getInfo),
        local.NewGetRoute("/version", r.getVersion),
        local.NewPostRoute("/auth", r.postAuth),
    }

    return r
}

// Routes return all the API routes dedicated to the docker system.
func (s *systemRouter) Routes() []router.Route {
    return s.routes
}

请注意,systemRouter实现了router.Router接口,并且Routes函数返回[]router.Route,它们的处理程序定义为:

func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error

替代Go标准的http处理程序:

func(w http.ResponseWriter, r *http.Request)

因此,他们有额外的代码将Docker API处理程序转换为Go HTTP处理程序,位于makeHttpHandler函数中。

最后,要将这些路由添加到他们的mux路由器中,在他们的server.go上,他们实现了几个其他函数来向处理程序添加中间件

如果您认为这就是您要寻找的内容,请花时间分析Docker代码以了解其路由,如果需要我进一步阐述或者我遗漏了任何内容,请发表评论。


1

因为我对Go语言还不熟悉,所以希望能提供一种简洁的解决方案。在每个模块中,我们可以创建一个Route函数,它期望一个主路由指针并创建其子路由。我们的main.go文件如下:

package main

import (
    "net/http"
    "github.com/user-name/repo-name/auth"
    "github.com/gorilla/mux"
)

func main() {
    r := mux.NewRouter()
    auth.Router(r)
    http.ListenAndServe(":8080", r)
}

然后在认证模块中,我们可以创建一个路由文件

package auth

import "github.com/gorilla/mux"

func Router(r *mux.Router) {
    routes := r.PathPrefix("/auth").Subrouter()

    routes.HandleFunc("/register", Register)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接