package router import ( "net/http" "os" "path/filepath" "server/internal/config" "server/internal/middleware" "server/internal/pkg/httputil" "github.com/go-chi/chi/v5" "go.uber.org/fx" ) type Group string const ( AdminPublicRoutes Group = "admin_public_routes" AdminRoutes Group = "admin_routes" SiteRoutes Group = "site_routes" ) type Registrar interface { Register(r chi.Router) } type Params struct { fx.In JWT *middleware.JWTMiddleware Auth *middleware.AuthMiddleware Logger *middleware.LoggerMiddleware AccessLog *middleware.AccessLogMiddleware RequestContext *middleware.RequestContextMiddleware Config *config.Config AdminPublicRoutes []Registrar `group:"admin_public_routes"` AdminRoutes []Registrar `group:"admin_routes"` SiteRoutes []Registrar `group:"site_routes"` } func NewRouter(p Params, cfg *config.Config) *chi.Mux { mux := chi.NewRouter() // 中间件顺序很重要 洋葱模型 越靠前的中间件 包裹范围越大 // 请求进入: // //A 前置代码 // | // B 前置代码 // | // C 前置代码 // | // Handler // C 后置代码 // B 后置代码 //A 后置代码 // //响应返回: mux.Use( p.RequestContext.Middleware, p.Logger.Middleware, p.AccessLog.Middleware, ) if config.IsDev() { // 开放静态目录 registerStaticFiles(mux, cfg.File.UploadDir) // 开发阶段:遍历所有已注册路由的清单接口(无鉴权) mux.Get("/api/admin/routes", ListRoutes(mux)) } mux.Route("/api/admin", func(r chi.Router) { // 免鉴权接口 for _, route := range p.AdminPublicRoutes { route.Register(r) } r.Group(func(r chi.Router) { // jwt 和 auth中间件 r.Use(p.JWT.Middleware) r.Use(p.Auth.Middleware(mux)) // 循环挂载所有后台业务模块 for _, route := range p.AdminRoutes { route.Register(r) } }) }) mux.Route("/api", func(r chi.Router) { for _, route := range p.SiteRoutes { route.Register(r) } }) return mux } func registerStaticFiles(r chi.Router, uploadDir string) { rootDir, _ := os.Getwd() uploadsDir := filepath.Join(rootDir, uploadDir) r.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.Dir(uploadsDir)))) } // RouteInfo 路由信息 type RouteInfo struct { Method string `json:"method"` Path string `json:"path"` } // ListRoutes 遍历所有已注册的路由 func ListRoutes(mux *chi.Mux) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var routes []RouteInfo err := chi.Walk(mux, func(method string, route string, _ http.Handler, _ ...func(http.Handler) http.Handler) error { routes = append(routes, RouteInfo{Method: method, Path: route}) return nil }) if err != nil { httputil.Fail(w, err) return } httputil.Ok(w, routes) } } func AsRegistrar(group Group, f any) any { return fx.Annotate( f, fx.As(new(Registrar)), fx.ResultTags(`group:"`+string(group)+`"`), ) }