路由基础 #

一、路由概述 #

路由是将HTTP请求映射到处理函数的机制。Echo使用基于基数树(Radix Tree)的高性能路由实现。

1.1 路由组成 #

text
┌─────────────────────────────────────────────────────────┐
│                    路由组成                              │
├─────────────────────────────────────────────────────────┤
│  HTTP方法 + 路径 + 处理函数 = 路由                       │
│                                                         │
│  GET     /users/:id   →  getUserHandler                 │
│  POST    /users       →  createUserHandler              │
│  PUT     /users/:id   →  updateUserHandler              │
│  DELETE  /users/:id   →  deleteUserHandler              │
└─────────────────────────────────────────────────────────┘

1.2 路由特点 #

特点 说明
高性能 基于基数树,零内存分配
支持参数 路径参数、查询参数
路由分组 支持路由前缀分组
中间件 支持路由级别中间件

二、基本路由 #

2.1 注册路由 #

go
package main

import (
    "net/http"
    "github.com/labstack/echo/v4"
)

func main() {
    e := echo.New()
    
    e.GET("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "GET /")
    })
    
    e.POST("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "POST /")
    })
    
    e.PUT("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "PUT /")
    })
    
    e.DELETE("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "DELETE /")
    })
    
    e.PATCH("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "PATCH /")
    })
    
    e.OPTIONS("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "OPTIONS /")
    })
    
    e.HEAD("/", func(c echo.Context) error {
        return c.NoContent(http.StatusOK)
    })
    
    e.Start(":8080")
}

2.2 Any方法 #

注册所有HTTP方法:

go
e.Any("/any", func(c echo.Context) error {
    return c.String(http.StatusOK, "Any method")
})

2.3 Match方法 #

注册指定方法:

go
e.Match([]string{http.MethodGet, http.MethodPost}, "/match", func(c echo.Context) error {
    return c.String(http.StatusOK, "GET or POST")
})

三、路径匹配 #

3.1 静态路径 #

go
e.GET("/users", getUsers)
e.GET("/users/profile", getProfile)
e.GET("/users/settings", getSettings)

3.2 路径参数 #

使用 :param 定义路径参数:

go
e.GET("/users/:id", func(c echo.Context) error {
    id := c.Param("id")
    return c.String(http.StatusOK, "User ID: "+id)
})

测试:

bash
curl http://localhost:8080/users/123
User ID: 123

3.3 多个路径参数 #

go
e.GET("/users/:userId/posts/:postId", func(c echo.Context) error {
    userId := c.Param("userId")
    postId := c.Param("postId")
    return c.String(http.StatusOK, "User: "+userId+", Post: "+postId)
})

测试:

bash
curl http://localhost:8080/users/1/posts/100
User: 1, Post: 100

3.4 通配符参数 #

使用 * 匹配任意路径:

go
e.GET("/files/*", func(c echo.Context) error {
    path := c.Param("*")
    return c.String(http.StatusOK, "File path: "+path)
})

测试:

bash
curl http://localhost:8080/files/docs/readme.md
File path: docs/readme.md

3.5 可选参数 #

Echo不直接支持可选参数,可以通过注册多个路由实现:

go
e.GET("/search", search)
e.GET("/search/:keyword", search)

func search(c echo.Context) error {
    keyword := c.Param("keyword")
    if keyword == "" {
        keyword = c.QueryParam("q")
    }
    return c.String(http.StatusOK, "Search: "+keyword)
}

四、路由优先级 #

4.1 匹配规则 #

Echo按以下优先级匹配路由:

text
1. 静态路径(最高优先级)
2. 路径参数
3. 通配符(最低优先级)

4.2 示例说明 #

go
e.GET("/users/profile", getProfile)
e.GET("/users/:id", getUser)
e.GET("/users/*", getAllUsers)

匹配结果:

请求路径 匹配路由 说明
/users/profile /users/profile 静态路径优先
/users/123 /users/:id 参数匹配
/users/admin/settings /users/* 通配符匹配

4.3 冲突检测 #

Echo会在启动时检测路由冲突:

go
e.GET("/users/:id", getUser)
e.GET("/users/:name", getByName)

启动时报错:

text
echo: path conflict at /users/:name

五、路由信息 #

5.1 获取路由列表 #

go
e := echo.New()

e.GET("/", home)
e.GET("/users", getUsers)
e.GET("/users/:id", getUser)

for _, r := range e.Routes() {
    fmt.Printf("%s %s\n", r.Method, r.Path)
}

输出:

text
GET /
GET /users
GET /users/:id

5.2 路由信息结构 #

go
type Route struct {
    Method string `json:"method"`
    Path   string `json:"path"`
    Name   string `json:"name"`
}

5.3 命名路由 #

go
e.GET("/users/:id", getUser).Name = "getUser"

url := e.Reverse("getUser", "123")
fmt.Println(url)

输出:

text
/users/123

六、路由分组 #

6.1 基本分组 #

go
g := e.Group("/api/v1")

g.GET("/users", getUsers)
g.GET("/users/:id", getUser)
g.POST("/users", createUser)

等价于:

go
e.GET("/api/v1/users", getUsers)
e.GET("/api/v1/users/:id", getUser)
e.POST("/api/v1/users", createUser)

6.2 嵌套分组 #

go
api := e.Group("/api")
v1 := api.Group("/v1")

v1.GET("/users", getUsers)
v1.GET("/posts", getPosts)

v2 := api.Group("/v2")

v2.GET("/users", getUsersV2)
v2.GET("/posts", getPostsV2)

6.3 分组中间件 #

go
api := e.Group("/api", middleware.Logger())

api.GET("/users", getUsers)

auth := api.Group("/auth", middleware.JWT([]byte("secret")))

auth.GET("/profile", getProfile)

七、路由处理函数 #

7.1 HandlerFunc类型 #

go
type HandlerFunc func(Context) error

7.2 返回错误 #

go
e.GET("/error", func(c echo.Context) error {
    return echo.NewHTTPError(http.StatusBadRequest, "参数错误")
})

7.3 自定义错误处理 #

go
e.HTTPErrorHandler = func(err error, c echo.Context) {
    code := http.StatusInternalServerError
    msg := "Internal Server Error"
    
    if he, ok := err.(*echo.HTTPError); ok {
        code = he.Code
        msg = he.Message.(string)
    }
    
    c.JSON(code, map[string]interface{}{
        "code":    code,
        "message": msg,
    })
}

八、路由限制 #

8.1 路由数量 #

Echo没有路由数量限制,但建议:

  • 单个应用路由数 < 1000
  • 使用分组组织路由
  • 避免过于复杂的路由结构

8.2 路径长度 #

  • 最大路径长度:无限制
  • 建议路径长度 < 200字符

8.3 参数数量 #

  • 单个路由参数数量:无限制
  • 建议参数数量 < 10

九、路由调试 #

9.1 打印路由表 #

go
func printRoutes(e *echo.Echo) {
    fmt.Println("\nRegistered Routes:")
    fmt.Println("==================")
    for _, r := range e.Routes() {
        fmt.Printf("%-6s %s\n", r.Method, r.Path)
    }
}

9.2 路由中间件 #

go
e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        fmt.Printf("Route: %s %s\n", c.Request().Method, c.Path())
        return next(c)
    }
})

十、完整示例 #

go
package main

import (
    "fmt"
    "net/http"
    "github.com/labstack/echo/v4"
)

func main() {
    e := echo.New()
    
    e.GET("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "Home")
    })
    
    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.GET("/:id/comments", getComments)
        }
    }
    
    e.GET("/files/*", func(c echo.Context) error {
        return c.String(http.StatusOK, "File: "+c.Param("*"))
    })
    
    printRoutes(e)
    
    e.Start(":8080")
}

func printRoutes(e *echo.Echo) {
    fmt.Println("\nRegistered Routes:")
    fmt.Println("==================")
    for _, r := range e.Routes() {
        fmt.Printf("%-6s %s\n", r.Method, r.Path)
    }
}

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)
}

func listPosts(c echo.Context) error {
    return c.JSON(http.StatusOK, []string{"post1", "post2"})
}

func getPost(c echo.Context) error {
    return c.JSON(http.StatusOK, map[string]string{"id": c.Param("id")})
}

func getComments(c echo.Context) error {
    return c.JSON(http.StatusOK, map[string]string{"postId": c.Param("id")})
}

十一、总结 #

路由基础要点:

概念 说明
HTTP方法 GET、POST、PUT、DELETE、PATCH等
静态路径 精确匹配,优先级最高
路径参数 :param 定义,动态匹配
通配符 * 匹配任意路径
路由分组 Group() 组织路由
路由优先级 静态 > 参数 > 通配符

准备好学习路由方法了吗?让我们进入下一章!

最后更新:2026-03-28