Golang中Gorilla mux嵌套路由的实现方法

Golang中Gorilla mux嵌套路由的实现方法 你好,

我们如何使用 Gorilla Mux 实现路由嵌套? 我有多个位于 /crons/ 下的定时任务,并希望根据文件名使用独立的函数来处理和执行每个定时任务。

package main

import (
	"fmt"
	"net/http"
	"time"

	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
)

func main() {
	route()
}
func hello(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Hello World !!!"))
}
func ProcessCrons(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Crons route"))
	/* 如何在这里编写多个路由? */
	//r.PathPrefix("/crons/add_data.php").HandlerFunc(AddDataCron).Methods("GET", "POST")
	//r.PathPrefix("/crons/remove_data.php").HandlerFunc(RemoveDataCron).Methods("GET", "POST")
}
func route() {
	r := mux.NewRouter()
	r.PathPrefix("/hello").HandlerFunc(hello).Methods("GET", "POST")
	r.PathPrefix("/crons/").HandlerFunc(ProcessCrons).Methods("GET", "POST")
	server := &http.Server{
		Addr:         ":8080",
		WriteTimeout: 15 * time.Second,
		ReadTimeout:  5 * time.Second,
		Handler:      handlers.CompressHandler(r),
	}
	err := server.ListenAndServe()
	if err != nil {
		fmt.Println(err)
	}
}

Playground 链接

我该如何实现?有人能帮忙吗?

编辑1:

根据文件结构,ProcessCrons() 位于另一个包中。为了解释方便,我将其包含在了 main 包下。


更多关于Golang中Gorilla mux嵌套路由的实现方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html

10 回复

这正是我一直在寻找的。谢谢。

更多关于Golang中Gorilla mux嵌套路由的实现方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


感谢您的澄清。将路由器分发到不同的包中有特定的原因吗?

感谢各位的回答。

应用程序包含多个独立的模块。每个模块将拥有多个路由/子路由。将所有内容放在同一个包下可能会有些难以管理。这就是为什么我尝试将它们放在不同的包中。

我应该使用哪个Go路由器?(附流程图)

https://www.alexedwards.net/blog/which-go-router-should-i-use

是的。但是我如何在 ProcessCrons() 中使用 SubRoute 呢?根据我的文件结构,ProcessCrons() 在另一个包中。我正尝试在那个包中创建子路由。

为了解释清楚,我不得不把它放在主包下。 抱歉,我应该早点说明这一点。

在这种情况下,你或许可以在路由器包中编写一个函数,用于为特定路由注册一个HandlerFunc。每个实现了处理器的包随后可以为所需的路由注册该处理器。

这样一来,路由代码就不需要为任何给定路由或子路由硬编码所有可能的处理器。它只需遍历已注册的路由和处理器列表,并将它们添加到路由器中。

为了最小化包之间的依赖关系,主包(或某个聚合包)可以根据需要进行连接。

实际上我也做过类似的事情,而且你的思路已经很接近了。在你的主包中创建一个子路由器,然后将这个子路由器传递给你其他包中的一个函数:

// 我们的主路由器
r := mux.NewRouter()
// Crons 子路由器
cronsRouter := r.PathPrefix("/crons").Subrouter()
// 将子路由器传递给我们的处理程序
CronsRouterHandler(cronsRouter)

… 然后,在你的 crons(或其他)包中:

// CronsRouterHandler 为 crons 提供内容服务。
func CronsRouterHandler(r *mux.Router) {
	// 请注意,由于这是一个子路由器,r.Path("/add_data.php")
	// 将映射到 /crons/add_data.php。调用此处理函数的包需要关注这一点。
	// 你也可以基于主机等条件来监听这些 crons 路由。
	r.Path("/add_data.php").Methods("GET", "POST").HandlerFunc(AddDataCron)
	r.Path("/remove_data.php").Methods("GET", "POST").HandlerFunc(RemoveDataCron)
}

我相信这应该能帮你实现想要的功能!

你好 @ganesh_salunkhe

你试过使用 Subrouter 吗?

来自文档:

func (*Route) Subrouter

func (r *Route) Subrouter() *Router

Subrouter 为路由创建一个子路由器。

只有在父路由匹配时,它才会测试内部路由。例如:

r := mux.NewRouter()
s := r.Host("www.example.com").Subrouter()
s.HandleFunc("/products/", ProductsHandler)
s.HandleFunc("/products/{key}", ProductHandler)
s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)

在这里,如果主机不匹配,子路由器中注册的路由将不会被测试。

听起来这应该符合你的使用场景,只不过你的情况会使用 PathPrefix() 而不是 Host()

在 Gorilla mux 中实现嵌套路由,可以使用 SubrouterPathPrefix 配合 Handler。以下是针对你需求的实现方案:

package main

import (
	"fmt"
	"net/http"
	"time"

	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
)

func main() {
	route()
}

func hello(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Hello World !!!"))
}

func AddDataCron(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Add Data Cron Executed"))
}

func RemoveDataCron(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Remove Data Cron Executed"))
}

func route() {
	r := mux.NewRouter()
	
	// 基础路由
	r.HandleFunc("/hello", hello).Methods("GET", "POST")
	
	// 创建 /crons/ 子路由
	cronsRouter := r.PathPrefix("/crons/").Subrouter()
	
	// 在子路由上注册具体的定时任务处理器
	cronsRouter.HandleFunc("/add_data.php", AddDataCron).Methods("GET", "POST")
	cronsRouter.HandleFunc("/remove_data.php", RemoveDataCron).Methods("GET", "POST")
	
	// 可选:为子路由添加中间件
	cronsRouter.Use(func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("X-Cron-Handler", "true")
			next.ServeHTTP(w, r)
		})
	})

	server := &http.Server{
		Addr:         ":8080",
		WriteTimeout: 15 * time.Second,
		ReadTimeout:  5 * time.Second,
		Handler:      handlers.CompressHandler(r),
	}
	
	err := server.ListenAndServe()
	if err != nil {
		fmt.Println(err)
	}
}

如果你的 ProcessCrons 函数需要处理所有 /crons/ 下的请求,可以这样实现:

package main

import (
	"fmt"
	"net/http"
	"time"

	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
)

func main() {
	route()
}

func hello(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Hello World !!!"))
}

func ProcessCrons(w http.ResponseWriter, r *http.Request) {
	// 获取请求路径
	vars := mux.Vars(r)
	
	// 根据文件名处理不同的定时任务
	switch r.URL.Path {
	case "/crons/add_data.php":
		w.Write([]byte("Processing Add Data Cron"))
	case "/crons/remove_data.php":
		w.Write([]byte("Processing Remove Data Cron"))
	default:
		w.Write([]byte("Unknown Cron Job"))
	}
}

func route() {
	r := mux.NewRouter()
	
	r.HandleFunc("/hello", hello).Methods("GET", "POST")
	
	// 使用 PathPrefix 匹配所有 /crons/ 开头的路径
	r.PathPrefix("/crons/").HandlerFunc(ProcessCrons).Methods("GET", "POST")
	
	server := &http.Server{
		Addr:         ":8080",
		WriteTimeout: 15 * time.Second,
		ReadTimeout:  5 * time.Second,
		Handler:      handlers.CompressHandler(r),
	}
	
	err := server.ListenAndServe()
	if err != nil {
		fmt.Println(err)
	}
}

对于跨包的情况,如果 ProcessCrons 在另一个包中,可以这样组织:

// 在 crons 包中
package crons

import "net/http"

func AddDataCron(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Add Data Cron Executed"))
}

func RemoveDataCron(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Remove Data Cron Executed"))
}

// 在 main.go 中
package main

import (
	"fmt"
	"net/http"
	"time"
	"yourproject/crons"

	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
)

func route() {
	r := mux.NewRouter()
	
	r.HandleFunc("/hello", hello).Methods("GET", "POST")
	
	cronsRouter := r.PathPrefix("/crons/").Subrouter()
	cronsRouter.HandleFunc("/add_data.php", crons.AddDataCron).Methods("GET", "POST")
	cronsRouter.HandleFunc("/remove_data.php", crons.RemoveDataCron).Methods("GET", "POST")
	
	server := &http.Server{
		Addr:         ":8080",
		WriteTimeout: 15 * time.Second,
		ReadTimeout:  5 * time.Second,
		Handler:      handlers.CompressHandler(r),
	}
	
	err := server.ListenAndServe()
	if err != nil {
		fmt.Println(err)
	}
}

使用 Subrouter 的优势:

  1. 可以独立为嵌套路由添加中间件
  2. 路径匹配更清晰
  3. 支持路由分组管理
  4. 可以复用路径前缀
回到顶部