Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

router.md

maoxiaoyue edited this page May 14, 2026 · 2 revisions

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 路由與傳統路由可並存,完全向後相容。

多協議 Schema 註冊

v0.8.5+ 多協議支援為 v0.8.5 新增功能。v0.8.1 僅支援 REST 協議。

Router 的 Schema 系統不僅限於 REST,同時支援 gRPC、Bot、MCP、WebSocket、CLI 六種協議。所有協議共用同一個 Schema Registry,AI 只需讀一份 manifest 就能理解整個應用的所有 API。

支援的協議一覽

協議 Command 格式 說明
rest Method + Path(自動) 傳統 HTTP RESTful API
grpc Service/Method gRPC 遠端呼叫(Protobuf)
bot /command 或事件名 聊天機器人(Telegram、Line、Discord 等)
mcp tool_name Model Context Protocol 工具
websocket message_type WebSocket 雙向訊息
cli command_name CLI 子命令

gRPC 範例

gRPC 方法透過 schema.RegisterGRPC() 註冊到全域 Registry,與 pkg/grpc Server 搭配使用:

import (
 "github.com/maoxiaoyue/hypgo/pkg/schema"
 hypgrpc "github.com/maoxiaoyue/hypgo/pkg/grpc"
)
// 定義 gRPC 請求/回應型別
type CreateUserReq struct {
 Name string `json:"name"`
 Email string `json:"email"`
}
type UserResp struct {
 ID int `json:"id"`
 Name string `json:"name"`
 Email string `json:"email"`
}
// 註冊 gRPC schema(Command 格式:Service/Method)
schema.RegisterGRPC("UserService/CreateUser", "建立使用者", CreateUserReq{}, UserResp{})
schema.RegisterGRPC("UserService/GetUser", "取得使用者資訊", GetUserReq{}, UserResp{})
schema.RegisterGRPC("UserService/ListUsers", "列出所有使用者", ListUsersReq{}, ListUsersResp{})
// 啟動 gRPC Server
grpcServer := hypgrpc.New(hypgrpc.Config{
 Addr: ":9090",
 EnableReflection: true,
 EnableHealthCheck: true,
 TLS: &hypgrpc.TLSConfig{
 CertFile: "cert.pem",
 KeyFile: "key.pem",
 },
}, logger)
// 註冊 gRPC 服務實作(標準 protobuf 服務)
pb.RegisterUserServiceServer(grpcServer.GRPCServer(), &userService{})
// 啟動
go grpcServer.Start()
defer grpcServer.GracefulStop()

gRPC Schema 在 Registry 中的查詢 key 為 grpc|UserService/CreateUser,可透過 schema.Global().GetByKey() 查詢,或用 schema.Global().GetByProtocol("grpc") 取得所有 gRPC 路由。

Bot 範例(多平台聊天機器人)

Bot schema 支援跨平台或平台專屬兩種模式:

// 跨平台通用指令
schema.RegisterBot("/start", "啟動機器人", nil, WelcomeMsg{})
schema.RegisterBot("/help", "顯示幫助", nil, HelpMsg{})
// Telegram 專屬指令
schema.RegisterBotPlatform("telegram", "/game", "開始遊戲", GameOpts{}, GameState{})
schema.RegisterBotPlatform("telegram", "/settings", "用戶設定", nil, SettingsResp{})
// Discord 專屬指令(使用 ! 前綴)
schema.RegisterBotPlatform("discord", "!play", "播放音樂", PlayReq{}, PlayResp{})
schema.RegisterBotPlatform("discord", "!queue", "顯示播放佇列", nil, QueueResp{})
// LINE 專屬事件
schema.RegisterBotPlatform("line", "follow", "用戶追蹤事件", nil, FollowResp{})

跨平台指令的 key 為 bot|/start,平台專屬指令會自動加入平台 tag 供篩選。

MCP 範例(Model Context Protocol)

MCP 用於定義 AI 工具呼叫的合約:

// MCP tool 註冊
schema.RegisterMCP("search_repos", "搜尋 GitHub 倉庫", SearchInput{}, SearchOutput{})
schema.RegisterMCP("create_issue", "建立 Issue", CreateIssueReq{}, IssueResp{})
schema.RegisterMCP("run_query", "執行資料庫查詢", QueryInput{}, QueryResult{})

WebSocket 範例

WebSocket 訊息類型透過 RegisterWebSocket 定義雙向通訊合約:

// WebSocket 訊息類型
schema.RegisterWebSocket("join_room", "加入房間", JoinReq{}, JoinResp{})
schema.RegisterWebSocket("send_message", "發送訊息", SendMsgReq{}, SendMsgResp{})
schema.RegisterWebSocket("leave_room", "離開房間", LeaveReq{}, nil)
schema.RegisterWebSocket("sync_state", "同步遊戲狀態", nil, GameState{})

CLI 範例

CLI 子命令的 schema 讓 AI 能理解命令列工具的輸入輸出:

// CLI 子命令
schema.RegisterCLI("process", "處理資料檔案", ProcessFlags{}, ProcessResult{})
schema.RegisterCLI("export", "匯出報表", ExportFlags{}, ExportResult{})

查詢已註冊的多協議 Schema

// 按協議篩選
grpcRoutes := schema.Global().GetByProtocol("grpc")
botRoutes := schema.Global().GetByProtocol("bot")
wsRoutes := schema.Global().GetByProtocol("websocket")
// 用完整 key 查詢
route, ok := schema.Global().GetByKey("grpc|UserService/CreateUser")
route, ok = schema.Global().GetByKey("bot|/start")
route, ok = schema.Global().GetByKey("mcp|search_repos")
route, ok = schema.Global().GetByKey("websocket|join_room")
// REST 向後相容查詢
route, ok = schema.Global().Get("POST", "/api/users")
// 取得全部(所有協議)
all := schema.Global().All()

完整多協議應用範例

一個同時提供 REST、gRPC、WebSocket、Bot 的完整應用:

func main() {
 r := router.New()
 // ── REST API ──
 r.Schema(schema.Route{
 Method: "POST",
 Path: "/api/users",
 Summary: "建立使用者",
 Tags: []string{"users"},
 Input: CreateUserReq{},
 Output: UserResp{},
 }).Handle(createUserHandler)
 r.Schema(schema.Route{
 Method: "GET",
 Path: "/api/users/:id",
 Summary: "取得使用者",
 Tags: []string{"users"},
 Output: UserResp{},
 }).Handle(getUserHandler)
 // ── gRPC ──
 schema.RegisterGRPC("UserService/CreateUser", "建立使用者", CreateUserReq{}, UserResp{})
 schema.RegisterGRPC("UserService/GetUser", "取得使用者", GetUserReq{}, UserResp{})
 // ── WebSocket ──
 schema.RegisterWebSocket("join_room", "加入房間", JoinReq{}, JoinResp{})
 schema.RegisterWebSocket("chat", "聊天訊息", ChatMsg{}, ChatMsg{})
 // ── Bot ──
 schema.RegisterBot("/start", "啟動機器人", nil, WelcomeMsg{})
 schema.RegisterBotPlatform("telegram", "/game", "開始遊戲", GameOpts{}, GameState{})
 // 產出的 Manifest 會包含以上所有協議的 API 描述
 // AI 讀一個 manifest 就能理解整個應用
 srv := server.New(cfg, r)
 srv.Start()
}

REST + gRPC 雙協議伺服器範例

常見場景:同一服務同時對外提供 REST API(給前端/第三方),對內提供 gRPC(給微服務):

func main() {
 r := router.New()
 log := logger.NewLogger()
 // REST API(對外)
 api := r.NewGroup("/api/v1")
 api.Schema(schema.Route{
 Method: "GET",
 Path: "/products",
 Summary: "取得商品列表",
 Tags: []string{"products"},
 Output: []ProductResp{},
 }).Handle(listProducts)
 api.Schema(schema.Route{
 Method: "POST",
 Path: "/orders",
 Summary: "建立訂單",
 Tags: []string{"orders"},
 Input: CreateOrderReq{},
 Output: OrderResp{},
 Responses: map[int]schema.ResponseSchema{
 201: {Description: "Order created"},
 400: {Description: "Invalid input"},
 409: {Description: "Insufficient stock"},
 },
 }).Handle(createOrder)
 // gRPC(對內微服務間通訊)
 schema.RegisterGRPC("OrderService/CreateOrder", "建立訂單(內部)", InternalOrderReq{}, OrderResp{})
 schema.RegisterGRPC("OrderService/UpdateStock", "更新庫存", UpdateStockReq{}, StockResp{})
 schema.RegisterGRPC("InventoryService/CheckStock", "庫存查詢", CheckStockReq{}, StockResp{})
 // 啟動 REST Server(HTTP/2 + TLS)
 srv := server.New(cfg, r)
 go srv.Start()
 // 啟動 gRPC Server
 grpcSrv := hypgrpc.New(hypgrpc.Config{
 Addr: ":9090",
 EnableReflection: true,
 EnableHealthCheck: true,
 }, log)
 pb.RegisterOrderServiceServer(grpcSrv.GRPCServer(), &orderService{})
 pb.RegisterInventoryServiceServer(grpcSrv.GRPCServer(), &inventoryService{})
 grpcSrv.Start()
}

路由資訊查詢

透過 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{} 分配。

HypGo

繁體中文 | English


中文文件

設計文件

套件

AI 協作工具鏈

CLI 命令


English Docs

Design Docs

Packages

AI Collaboration Toolchain

CLI Commands

Clone this wiki locally

AltStyle によって変換されたページ (->オリジナル) /