router.md - maoxiaoyue/hypgo GitHub Wiki

Router Package (pkg/router)

router 套件為 HypGo 實現了核心的路由分發機制。它基於高效的基數樹(Radix Tree)實作,擁有極快的路由比對速度,支援動態參數擷取與萬用字元配對,同時將路由群組管理整合在內,大幅簡化大型應用的 API 結構組織。

主要特色

  • 零配置的高效能 Radix Tree: 自動化建立前綴樹進行路由檢索,執行速度遠勝一般基於 Regular Expression 的配對方式。
  • 支援 RESTful 參數與萬用字元: 如 :id(名稱配對)、*filepath(萬用配對)。
  • 內建記憶體快取 (LRU Route Cache): 若啟動時啟用,可以快取頻繁造訪的路由解析結果,使極高併發的路由比對效能再提升一個量級。
  • 靈活的中間件與群組 (Group): 透過路由群組管理特定前綴的路由,並且能夠為全域或是特定的 Group 插入中介軟體(Middleware)。
  • 嚴格或寬鬆的斜線處理 (Strict Slash): 可配置是否將 /path/path/ 視作同一路徑並自動重新導向。
  • 無縫整合 HTTP/3: 提供 EnableHTTP3 機制讓 HTTP/3 與 QUIC 原生整合進路由的支援中。

基礎使用

初始化一個路由器並加入簡單的路徑:

package main

import (
	"github.com/maoxiaoyue/hypgo/pkg/context"
	"github.com/maoxiaoyue/hypgo/pkg/router"
)

func main() {
	// 使用預設配置建立路由器
	r := router.New()

	// 基礎的 GET / POST
	r.GET("/ping", func(c *context.Context) {
		c.String(200, "pong")
	})

	r.POST("/submit", func(c *context.Context) {
		c.String(200, "Submitted!")
	})
	
	// 在 main 裡面搭配 Http Server 啟動 (由 pkg/server 負責)
}

路由參數配對

存取由 :name*action 定義的路徑參數:

// 匹配 /user/alice, /user/bob 等
r.GET("/user/:name", func(c *context.Context) {
	name := c.Param("name")
	c.String(200, "Hello %s", name)
})

// 匹配 /files/js/main.js 或 /files/css/style.css
r.GET("/files/*filepath", func(c *context.Context) {
	path := c.Param("filepath")
	c.String(200, "Requested file path: %s", path)
})

路由群組 (Group)

透過路由群組,能很方便的將不同業務邏輯切割開來:

api := r.NewGroup("/api/v1")
{
	users := api.NewGroup("/users")
	{
		users.GET("/", listUsers)        // /api/v1/users/
		users.GET("/:id", getUser)       // /api/v1/users/:id
		users.POST("/", createUser)      // /api/v1/users/
	}

	orders := api.NewGroup("/orders")
	{
		orders.GET("/", listOrders)      // /api/v1/orders/
		orders.GET("/:id", getOrder)     // /api/v1/orders/:id
	}
}

全域與群組中間件

使用 Use 可以在不同層級掛載 hypcontext.HandlerFunc

// 全域套用:所有經過此路由器的請求都會先經過 Middleware1, Middleware2
r.Use(Middleware1, Middleware2)

// 單獨掛載:僅掛載於 api 群組
api := r.NewGroup("/api/v1")
api.Use(AuthMiddleware)
api.GET("/secure", secureHandler) // 必須經過 AuthMiddleware 才會執行

客製化配置

router.New 也開放以函數式選項進行詳細設定:

opts := []router.RouterOption{
    router.WithCache(1000),             // 啟用 LRU Route Cache,快取 1000 條結果
    router.WithStrictSlash(true),       // 嚴格區分結尾斜線
    router.WithMethodNotAllowed(true),  // 自動捕捉並回應 405 Method Not Allowed
}
r := router.New(opts...)

Schema-first 路由(AI 協作)

除了傳統的路由註冊,Router 支援 Schema() 方法,讓路由攜帶 metadata(輸入/輸出型別、描述、標籤),供 AI 理解 API 行為、Manifest 生成與 Contract Testing 使用:

import "github.com/maoxiaoyue/hypgo/pkg/schema"

// 定義帶 metadata 的路由
r.Schema(schema.Route{
    Method:  "POST",
    Path:    "/api/users",
    Summary: "建立使用者",
    Tags:    []string{"users"},
    Input:   CreateUserRequest{},
    Output:  UserResponse{},
    Responses: map[int]schema.ResponseSchema{
        201: {Description: "User created"},
        400: {Description: "Invalid input"},
    },
}).Handle(createUserHandler)

Group 也支援 Schema,會自動加上 basePath 前綴:

api := r.NewGroup("/api/v1")
api.Schema(schema.Route{
    Method:  "GET",
    Path:    "/products",
    Summary: "取得商品列表",
}).Handle(listProductsHandler)
// → 自動註冊為 GET /api/v1/products

Schema 路由與傳統路由可並存,完全向後相容。

路由資訊查詢

透過 Routes() 取得所有已註冊的路由資訊(含 handler 函式名稱):

for _, route := range r.Routes() {
    fmt.Printf("%s %s → %v\n", route.Method, route.Path, route.HandlerNames)
}
// GET /health → [main.healthHandler]
// POST /api/users → [controllers.CreateUser]

HandlerNames 透過 runtime.FuncForPC 反射取得,供 Manifest 和偵錯使用。

HEAD 自動回應

若路由只註冊了 GET handler,HEAD 請求會自動使用 GET handler 回應(不需重複註冊):

r.GET("/api/status", statusHandler)
// HEAD /api/status → 自動使用 statusHandler(回應不含 body)

尾部斜線重導向

啟用 WithStrictSlash(true) 時,自動將 /path/path/ 重導向(HTTP 301):

r := router.New(router.WithStrictSlash(true))
r.GET("/users", listUsers)
// GET /users/  → 301 重導向到 /users
// GET /users   → 正常回應

GC 優化

Router 在高併發路徑上做了兩項 GC 優化:

Params Pool

makeContextParams() 使用 hypcontext.AcquireParams() 從 pool 取得 Params slice,避免每請求 make(Params, n) 分配。路由參數在請求結束後歸還 pool。

RouteCache cacheItem Pool

LRU 快取的 cacheItem 使用 sync.Pool 管理。新增快取項目時從 pool 取得,淘汰時清空欄位後歸還,消除頻繁的 &cacheItem{} 分配。