feat: use router to group routes
This commit is contained in:
parent
a3f4661456
commit
280cb93648
12 changed files with 222 additions and 198 deletions
|
|
@ -1,21 +1,29 @@
|
|||
package router
|
||||
|
||||
import "net/http"
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
type Middleware func(http.Handler) http.Handler
|
||||
"git.juancwu.dev/juancwu/budgit/internal/middleware"
|
||||
)
|
||||
|
||||
type Group struct {
|
||||
prefix string
|
||||
middleware []Middleware
|
||||
middleware []middleware.Middleware
|
||||
limiter *middleware.RateLimiter
|
||||
parent *Group
|
||||
mux *http.ServeMux
|
||||
}
|
||||
|
||||
func newGroup(mux *http.ServeMux, prefix string, mw []Middleware) *Group {
|
||||
return &Group{prefix: prefix, middleware: mw, mux: mux}
|
||||
func (g *Group) Use(mw ...middleware.Middleware) {
|
||||
g.middleware = append(g.middleware, mw...)
|
||||
}
|
||||
|
||||
func (g *Group) Use(mw ...Middleware) {
|
||||
g.middleware = append(g.middleware, mw...)
|
||||
// RateLimit sets a rate limit on this group. It runs before any middleware
|
||||
// in the chain, including inherited middleware from parent groups.
|
||||
// Parent group rate limits are checked first (root → leaf order).
|
||||
func (g *Group) RateLimit(limit int, window time.Duration) {
|
||||
g.limiter = middleware.NewRateLimiter(limit, window)
|
||||
}
|
||||
|
||||
type Method string
|
||||
|
|
@ -28,78 +36,120 @@ const (
|
|||
MethodPatch Method = "PATCH"
|
||||
)
|
||||
|
||||
func (g *Group) Handle(method Method, path string, handler http.HandlerFunc) {
|
||||
func (g *Group) Handle(method Method, path string, handler http.HandlerFunc, mw ...middleware.Middleware) {
|
||||
// Build chain: [rate limiters root→self] → [middleware root→self] → [route mw] → handler
|
||||
rateLimiters := g.collectRateLimiters()
|
||||
middlewares := g.collectMiddleware()
|
||||
middlewares = append(middlewares, mw...)
|
||||
|
||||
chain := append(rateLimiters, middlewares...)
|
||||
|
||||
pattern := string(method) + " " + g.prefix + path
|
||||
wrapped := chain(handler, g.middleware)
|
||||
wrapped := middleware.Chain(handler, chain...)
|
||||
g.mux.Handle(pattern, wrapped)
|
||||
}
|
||||
func (g *Group) Get(path string, h http.HandlerFunc) { g.Handle(MethodGet, path, h) }
|
||||
func (g *Group) Post(path string, h http.HandlerFunc) { g.Handle(MethodPost, path, h) }
|
||||
func (g *Group) Put(path string, h http.HandlerFunc) { g.Handle(MethodPut, path, h) }
|
||||
func (g *Group) Patch(path string, h http.HandlerFunc) { g.Handle(MethodPatch, path, h) }
|
||||
func (g *Group) Delete(path string, h http.HandlerFunc) { g.Handle(MethodDelete, path, h) }
|
||||
|
||||
// SubGroup creates a nested group with accumulated prefix and middleware.
|
||||
// Middleware added inside fn does not affect the parent group.
|
||||
func (g *Group) Get(path string, h http.HandlerFunc, mw ...middleware.Middleware) {
|
||||
g.Handle(MethodGet, path, h, mw...)
|
||||
}
|
||||
func (g *Group) Post(path string, h http.HandlerFunc, mw ...middleware.Middleware) {
|
||||
g.Handle(MethodPost, path, h, mw...)
|
||||
}
|
||||
func (g *Group) Put(path string, h http.HandlerFunc, mw ...middleware.Middleware) {
|
||||
g.Handle(MethodPut, path, h, mw...)
|
||||
}
|
||||
func (g *Group) Patch(path string, h http.HandlerFunc, mw ...middleware.Middleware) {
|
||||
g.Handle(MethodPatch, path, h, mw...)
|
||||
}
|
||||
func (g *Group) Delete(path string, h http.HandlerFunc, mw ...middleware.Middleware) {
|
||||
g.Handle(MethodDelete, path, h, mw...)
|
||||
}
|
||||
|
||||
// SubGroup creates a nested group. It inherits rate limits and middleware
|
||||
// from the parent via the parent pointer (not by copying).
|
||||
func (g *Group) SubGroup(prefix string, fn func(*Group)) {
|
||||
mw := make([]Middleware, len(g.middleware))
|
||||
copy(mw, g.middleware)
|
||||
sub := newGroup(g.mux, g.prefix+prefix, mw)
|
||||
sub := &Group{
|
||||
prefix: g.prefix + prefix,
|
||||
parent: g,
|
||||
mux: g.mux,
|
||||
}
|
||||
fn(sub)
|
||||
}
|
||||
|
||||
// RouteGroup is implemented by feature modules to register their routes.
|
||||
type RouteGroup interface {
|
||||
Prefix() string
|
||||
Register(g *Group)
|
||||
// collectRateLimiters walks up the parent chain and returns rate limit
|
||||
// middleware in root → leaf order.
|
||||
func (g *Group) collectRateLimiters() []middleware.Middleware {
|
||||
var result []middleware.Middleware
|
||||
if g.parent != nil {
|
||||
result = g.parent.collectRateLimiters()
|
||||
}
|
||||
if g.limiter != nil {
|
||||
result = append(result, g.limiter.Middleware())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// MiddlewareProvider is optionally implemented by RouteGroups that need
|
||||
// group-level middleware.
|
||||
type MiddlewareProvider interface {
|
||||
Middlewares() []Middleware
|
||||
// collectMiddleware walks up the parent chain and returns middleware
|
||||
// in root → leaf order.
|
||||
func (g *Group) collectMiddleware() []middleware.Middleware {
|
||||
var result []middleware.Middleware
|
||||
if g.parent != nil {
|
||||
result = g.parent.collectMiddleware()
|
||||
}
|
||||
result = append(result, g.middleware...)
|
||||
return result
|
||||
}
|
||||
|
||||
type Router struct {
|
||||
mux *http.ServeMux
|
||||
middleware []Middleware
|
||||
root *Group
|
||||
mux *http.ServeMux
|
||||
}
|
||||
|
||||
func New() *Router {
|
||||
return &Router{mux: http.NewServeMux()}
|
||||
mux := http.NewServeMux()
|
||||
return &Router{
|
||||
mux: mux,
|
||||
root: &Group{
|
||||
mux: mux,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Router) Mux() *http.ServeMux {
|
||||
return r.mux
|
||||
}
|
||||
|
||||
func (r *Router) Use(mw ...Middleware) {
|
||||
r.middleware = append(r.middleware, mw...)
|
||||
func (r *Router) Use(mw ...middleware.Middleware) {
|
||||
r.root.Use(mw...)
|
||||
}
|
||||
|
||||
// Mount registers one or more RouteGroups.
|
||||
func (r *Router) Mount(groups ...RouteGroup) {
|
||||
for _, rg := range groups {
|
||||
var mw []Middleware
|
||||
if mp, ok := rg.(MiddlewareProvider); ok {
|
||||
mw = mp.Middlewares()
|
||||
}
|
||||
g := newGroup(r.mux, rg.Prefix(), mw)
|
||||
rg.Register(g)
|
||||
}
|
||||
// Group creates a route group that inherits global middleware from the router.
|
||||
func (r *Router) Group(prefix string, fn func(*Group)) {
|
||||
r.root.SubGroup(prefix, fn)
|
||||
}
|
||||
|
||||
// Handler returns the final http.Handler with global middleware applied.
|
||||
// Handler returns the final http.Handler. All middleware is already applied
|
||||
// per-route through the group hierarchy, so this just returns the mux.
|
||||
func (r *Router) Handler() http.Handler {
|
||||
if len(r.middleware) == 0 {
|
||||
return r.mux
|
||||
}
|
||||
return chain(r.mux, r.middleware)
|
||||
return r.mux
|
||||
}
|
||||
|
||||
func chain(base http.Handler, mws []Middleware) http.Handler {
|
||||
for i := len(mws) - 1; i >= 0; i-- {
|
||||
base = mws[i](base)
|
||||
}
|
||||
return base
|
||||
func (r *Router) Handle(method Method, path string, handler http.HandlerFunc, mw ...middleware.Middleware) {
|
||||
r.root.Handle(method, path, handler, mw...)
|
||||
}
|
||||
|
||||
func (r *Router) Get(path string, h http.HandlerFunc, mw ...middleware.Middleware) {
|
||||
r.root.Get(path, h, mw...)
|
||||
}
|
||||
func (r *Router) Post(path string, h http.HandlerFunc, mw ...middleware.Middleware) {
|
||||
r.root.Post(path, h, mw...)
|
||||
}
|
||||
func (r *Router) Put(path string, h http.HandlerFunc, mw ...middleware.Middleware) {
|
||||
r.root.Put(path, h, mw...)
|
||||
}
|
||||
func (r *Router) Patch(path string, h http.HandlerFunc, mw ...middleware.Middleware) {
|
||||
r.root.Patch(path, h, mw...)
|
||||
}
|
||||
func (r *Router) Delete(path string, h http.HandlerFunc, mw ...middleware.Middleware) {
|
||||
r.root.Delete(path, h, mw...)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue