路由分组 #
一、路由分组概述 #
路由分组是组织和管理路由的重要方式,可以统一设置前缀和中间件。
1.1 分组的作用 #
text
┌─────────────────────────────────────────────────────────┐
│ 路由分组的作用 │
├─────────────────────────────────────────────────────────┤
│ │
│ 1. 统一路径前缀 │
│ /api/v1/users │
│ /api/v1/posts │
│ │
│ 2. 统一中间件 │
│ 所有 /api 路由都需要认证 │
│ │
│ 3. 版本控制 │
│ /api/v1/... │
│ /api/v2/... │
│ │
│ 4. 模块化组织 │
│ 按功能模块分组路由 │
│ │
└─────────────────────────────────────────────────────────┘
二、基本分组 #
2.1 创建分组 #
go
e := echo.New()
api := e.Group("/api")
api.GET("/users", getUsers)
api.GET("/posts", getPosts)
等价于:
go
e.GET("/api/users", getUsers)
e.GET("/api/posts", getPosts)
2.2 分组路由方法 #
分组支持所有HTTP方法:
go
api := e.Group("/api")
api.GET("/users", getUsers)
api.POST("/users", createUser)
api.PUT("/users/:id", updateUser)
api.DELETE("/users/:id", deleteUser)
2.3 分组与直接注册对比 #
go
// 方式一:直接注册
e.GET("/api/v1/users", getUsers)
e.GET("/api/v1/posts", getPosts)
e.GET("/api/v1/comments", getComments)
// 方式二:使用分组
api := e.Group("/api/v1")
api.GET("/users", getUsers)
api.GET("/posts", getPosts)
api.GET("/comments", getComments)
三、嵌套分组 #
3.1 多层嵌套 #
go
e := echo.New()
api := e.Group("/api")
v1 := api.Group("/v1")
users := v1.Group("/users")
users.GET("", listUsers)
users.GET("/:id", getUser)
users.POST("", createUser)
生成的路由:
text
/api/v1/users
/api/v1/users/:id
/api/v1/users
3.2 版本控制 #
go
e := echo.New()
api := e.Group("/api")
v1 := api.Group("/v1")
v1.GET("/users", getUsersV1)
v1.GET("/posts", getPostsV1)
v2 := api.Group("/v2")
v2.GET("/users", getUsersV2)
v2.GET("/posts", getPostsV2)
3.3 模块分组 #
go
e := echo.New()
api := e.Group("/api/v1")
users := api.Group("/users")
{
users.GET("", listUsers)
users.GET("/:id", getUser)
users.POST("", createUser)
users.PUT("/:id", updateUser)
users.DELETE("/:id", deleteUser)
}
posts := api.Group("/posts")
{
posts.GET("", listPosts)
posts.GET("/:id", getPost)
posts.POST("", createPost)
}
comments := api.Group("/comments")
{
comments.GET("", listComments)
comments.POST("", createComment)
}
四、分组中间件 #
4.1 分组级别中间件 #
go
e := echo.New()
api := e.Group("/api", middleware.Logger(), middleware.Recover())
api.GET("/users", getUsers)
api.GET("/posts", getPosts)
4.2 嵌套分组中间件 #
go
e := echo.New()
api := e.Group("/api")
api.Use(middleware.Logger())
v1 := api.Group("/v1")
v1.Use(middleware.RateLimiter())
users := v1.Group("/users")
users.Use(middleware.JWT([]byte("secret")))
users.GET("/profile", getProfile)
中间件执行顺序:
text
Logger → RateLimiter → JWT → Handler
4.3 认证分组 #
go
e := echo.New()
public := e.Group("/api/v1")
{
public.POST("/login", login)
public.POST("/register", register)
public.GET("/health", health)
}
protected := e.Group("/api/v1")
protected.Use(middleware.JWT([]byte("secret")))
{
protected.GET("/profile", getProfile)
protected.PUT("/profile", updateProfile)
protected.GET("/users", listUsers)
}
4.4 管理员分组 #
go
e := echo.New()
api := e.Group("/api/v1")
admin := api.Group("/admin")
admin.Use(middleware.JWT([]byte("secret")))
admin.Use(adminOnlyMiddleware)
{
admin.GET("/users", listAllUsers)
admin.DELETE("/users/:id", deleteUser)
admin.GET("/stats", getStats)
}
func adminOnlyMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
user := c.Get("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
if claims["role"] != "admin" {
return echo.NewHTTPError(http.StatusForbidden, "需要管理员权限")
}
return next(c)
}
}
五、路由组织最佳实践 #
5.1 按功能模块分组 #
go
func setupRoutes(e *echo.Echo) {
api := e.Group("/api/v1")
setupUserRoutes(api)
setupPostRoutes(api)
setupAuthRoutes(api)
setupAdminRoutes(api)
}
func setupUserRoutes(g *echo.Group) {
users := g.Group("/users")
users.GET("", listUsers)
users.GET("/:id", getUser)
users.POST("", createUser)
users.PUT("/:id", updateUser)
users.DELETE("/:id", deleteUser)
}
func setupPostRoutes(g *echo.Group) {
posts := g.Group("/posts")
posts.GET("", listPosts)
posts.GET("/:id", getPost)
posts.POST("", createPost)
posts.PUT("/:id", updatePost)
posts.DELETE("/:id", deletePost)
posts.GET("/:id/comments", getComments)
posts.POST("/:id/comments", createComment)
}
func setupAuthRoutes(g *echo.Group) {
auth := g.Group("/auth")
auth.POST("/login", login)
auth.POST("/register", register)
auth.POST("/logout", logout)
auth.POST("/refresh", refreshToken)
}
func setupAdminRoutes(g *echo.Group) {
admin := g.Group("/admin")
admin.Use(middleware.JWT([]byte("secret")))
admin.Use(adminOnlyMiddleware)
admin.GET("/users", listAllUsers)
admin.GET("/stats", getStats)
admin.GET("/logs", getLogs)
}
5.2 分离到独立文件 #
routes/user.go
go
package routes
import (
"github.com/labstack/echo/v4"
)
func SetupUserRoutes(g *echo.Group) {
users := g.Group("/users")
users.GET("", listUsers)
users.GET("/:id", getUser)
users.POST("", createUser)
users.PUT("/:id", updateUser)
users.DELETE("/:id", deleteUser)
}
func listUsers(c echo.Context) error {
return c.JSON(http.StatusOK, []string{"user1", "user2"})
}
func getUser(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]string{"id": c.Param("id")})
}
func createUser(c echo.Context) error {
return c.JSON(http.StatusCreated, map[string]string{"status": "created"})
}
func updateUser(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]string{"id": c.Param("id")})
}
func deleteUser(c echo.Context) error {
return c.NoContent(http.StatusNoContent)
}
routes/auth.go
go
package routes
import (
"github.com/labstack/echo/v4"
)
func SetupAuthRoutes(g *echo.Group) {
auth := g.Group("/auth")
auth.POST("/login", login)
auth.POST("/register", register)
auth.POST("/logout", logout)
}
main.go
go
package main
import (
"myapp/routes"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
func main() {
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
api := e.Group("/api/v1")
routes.SetupUserRoutes(api)
routes.SetupAuthRoutes(api)
e.Start(":8080")
}
六、版本控制 #
6.1 URL路径版本 #
go
e := echo.New()
v1 := e.Group("/api/v1")
v1.GET("/users", getUsersV1)
v2 := e.Group("/api/v2")
v2.GET("/users", getUsersV2)
6.2 请求头版本 #
go
e := echo.New()
e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
version := c.Request().Header.Get("API-Version")
c.Set("version", version)
return next(c)
}
})
e.GET("/users", func(c echo.Context) error {
version := c.Get("version").(string)
if version == "v2" {
return getUsersV2(c)
}
return getUsersV1(c)
})
6.3 版本废弃处理 #
go
e := echo.New()
v1 := e.Group("/api/v1")
v1.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
c.Response().Header().Set("X-API-Deprecated", "true")
c.Response().Header().Set("X-API-Sunset", "2024-12-31")
return next(c)
}
})
v1.GET("/users", getUsersV1)
v2 := e.Group("/api/v2")
v2.GET("/users", getUsersV2)
七、分组路由信息 #
7.1 获取分组路由 #
go
e := echo.New()
api := e.Group("/api")
api.GET("/users", getUsers)
api.GET("/posts", getPosts)
for _, r := range e.Routes() {
fmt.Printf("%s %s\n", r.Method, r.Path)
}
7.2 命名分组路由 #
go
e := echo.New()
api := e.Group("/api")
api.GET("/users", getUsers).Name = "users.list"
api.GET("/users/:id", getUser).Name = "users.get"
url := e.Reverse("users.get", "123")
fmt.Println(url)
八、完整示例 #
go
package main
import (
"net/http"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
func main() {
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.Use(middleware.CORS())
e.GET("/health", health)
api := e.Group("/api/v1")
{
setupAuthRoutes(api)
setupUserRoutes(api)
setupPostRoutes(api)
setupAdminRoutes(api)
}
e.Logger.Fatal(e.Start(":8080"))
}
func health(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]string{
"status": "healthy",
})
}
func setupAuthRoutes(g *echo.Group) {
auth := g.Group("/auth")
auth.POST("/login", login)
auth.POST("/register", register)
auth.POST("/logout", logout)
}
func setupUserRoutes(g *echo.Group) {
users := g.Group("/users")
users.GET("", listUsers)
users.GET("/:id", getUser)
users.POST("", createUser)
users.PUT("/:id", updateUser)
users.DELETE("/:id", deleteUser)
users.GET("/:id/posts", getUserPosts)
}
func setupPostRoutes(g *echo.Group) {
posts := g.Group("/posts")
posts.GET("", listPosts)
posts.GET("/:id", getPost)
posts.POST("", createPost)
posts.PUT("/:id", updatePost)
posts.DELETE("/:id", deletePost)
posts.GET("/:id/comments", getComments)
posts.POST("/:id/comments", createComment)
}
func setupAdminRoutes(g *echo.Group) {
admin := g.Group("/admin")
admin.Use(middleware.JWT([]byte("secret")))
admin.Use(adminOnlyMiddleware)
admin.GET("/users", listAllUsers)
admin.DELETE("/users/:id", deleteUser)
admin.GET("/stats", getStats)
admin.GET("/logs", getLogs)
}
func adminOnlyMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
return next(c)
}
}
func login(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]string{"token": "jwt-token"})
}
func register(c echo.Context) error {
return c.JSON(http.StatusCreated, map[string]string{"status": "created"})
}
func logout(c echo.Context) error {
return c.NoContent(http.StatusNoContent)
}
func listUsers(c echo.Context) error {
return c.JSON(http.StatusOK, []map[string]string{
{"id": "1", "name": "张三"},
{"id": "2", "name": "李四"},
})
}
func getUser(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]string{"id": c.Param("id")})
}
func createUser(c echo.Context) error {
return c.JSON(http.StatusCreated, map[string]string{"status": "created"})
}
func updateUser(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]string{"id": c.Param("id")})
}
func deleteUser(c echo.Context) error {
return c.NoContent(http.StatusNoContent)
}
func getUserPosts(c echo.Context) error {
return c.JSON(http.StatusOK, []map[string]string{
{"id": "1", "title": "文章1"},
})
}
func listPosts(c echo.Context) error {
return c.JSON(http.StatusOK, []map[string]string{})
}
func getPost(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]string{"id": c.Param("id")})
}
func createPost(c echo.Context) error {
return c.JSON(http.StatusCreated, map[string]string{"status": "created"})
}
func updatePost(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]string{"id": c.Param("id")})
}
func deletePost(c echo.Context) error {
return c.NoContent(http.StatusNoContent)
}
func getComments(c echo.Context) error {
return c.JSON(http.StatusOK, []map[string]string{})
}
func createComment(c echo.Context) error {
return c.JSON(http.StatusCreated, map[string]string{"status": "created"})
}
func listAllUsers(c echo.Context) error {
return c.JSON(http.StatusOK, []map[string]string{})
}
func getStats(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]int{"users": 100})
}
func getLogs(c echo.Context) error {
return c.JSON(http.StatusOK, []string{})
}
九、总结 #
路由分组要点:
| 功能 | 方法 | 示例 |
|---|---|---|
| 创建分组 | Group() |
g := e.Group("/api") |
| 分组路由 | GET/POST/... |
g.GET("/users", handler) |
| 分组中间件 | Use() |
g.Use(middleware) |
| 嵌套分组 | Group() |
v1 := g.Group("/v1") |
最佳实践:
- 按功能模块分组:users、posts、auth等
- 版本控制:/api/v1、/api/v2
- 中间件分层:公共中间件、认证中间件
- 分离路由文件:便于维护
准备好学习中间件了吗?让我们进入下一章!
最后更新:2026-03-28