9.1 Router 层是什么Router 层负责把 URL 和 HTTP 方法映射到对应的 Controller 方法。Express 的Router可以把路由分组管理而不是把所有路由都堆在app.ts里。9.2 基本用法最简单的写法import{Router}fromexpress;import{ProjectController}from/controllers/project/project-controller.js;exportfunctioncreateProjectRouter(controller:ProjectController){constrouterRouter();router.get(/projects,controller.list);router.get(/projects/:id,controller.get);router.post(/projects,controller.create);router.put(/projects/:id,controller.update);router.delete(/projects/:id,controller.remove);returnrouter;}用前缀分组同一个模块的路由共享前缀exportfunctioncreateProjectRouter(controller:ProjectController){constrouterRouter();// 所有路由自动带 /projects 前缀router.get(/,controller.list);// GET /projectsrouter.get(/:id,controller.get);// GET /projects/:idrouter.put(/,controller.create);// PUT /projectsrouter.put(/:id,controller.update);// PUT /projects/:idrouter.delete(/:id,controller.remove);// DELETE /projects/:idreturnrouter;}然后在父路由里挂上前缀router.use(/projects,createProjectRouter(projectController));9.3 路由的层级组织按版本 按模块大型项目推荐这样组织src/routers/ ├── index.ts# 根路由挂载所有子路由├── api-router.ts# /api 总路由├── v1/ │ └── index.ts# /api/v1 版本路由├── v2/ │ └── index.ts# /api/v2 版本路由├── project/ │ └── project-router.ts# 项目管理模块路由└── user/ └── user-router.ts# 用户管理模块路由根路由src/routers/index.tsimport{Router}fromexpress;import{createApiRouter}from./api-router.js;exportfunctioncreateRouter(){constrouterRouter();// API 路由router.use(/api,createApiRouter());// 健康检查不放在 /api 下也行router.get(/health,(_req,res){res.json({status:ok});});returnrouter;}API 总路由src/routers/api-router.tsimport{Router}fromexpress;import{createV1Router}from./v1/index.js;exportfunctioncreateApiRouter(){constrouterRouter();router.use(/v1,createV1Router());// router.use(/v2, createV2Router()); // 以后加 v2 版本returnrouter;}版本路由src/routers/v1/index.tsimport{Router}fromexpress;importtype{AppModels}from/bootstrap.js;import{ProjectController}from/controllers/project/project-controller.js;import{UserController}from/controllers/user/user-controller.js;import{ProjectService}from/services/project/project-service.js;import{UserService}from/services/user/user-service.js;import{createProjectRouter}from../project/project-router.js;import{createUserRouter}from../user/user-router.js;exportfunctioncreateV1Router(models:AppModels){constrouterRouter();// 组装各模块的 Controller 和 ServiceconstprojectControllernewProjectController(newProjectService(models.projects),);constuserControllernewUserController(newUserService(models.users),);// 挂载各模块路由router.use(/projects,createProjectRouter(projectController));router.use(/users,createUserRouter(userController));returnrouter;}模块路由src/routers/project/project-router.tsimport{Router}fromexpress;importtype{ProjectController}from/controllers/project/project-controller.js;exportfunctioncreateProjectRouter(controller:ProjectController){constrouterRouter();router.get(/,controller.list);router.get(/:id,controller.get);router.put(/,controller.create);router.put(/:id,controller.update);router.delete(/:id,controller.remove);returnrouter;}9.4 为什么在版本路由里组装 Controller你可能注意到了Controller 和 Service 的组装new放在了版本路由里而不是bootstrap.ts。两种做法都可以做法优点缺点bootstrap 里组装全部统一管理所有依赖一目了然bootstrap 文件会越来越长各版本路由里组装每个版本自己决定用哪些模块不同版本可以有不同实现依赖分散在多处中小项目推荐放 bootstrap 统一管理大项目可以按模块/按版本分散。本教程为了简单清晰采用集中在 bootstrap 组装的方式见第 10 篇。9.5 路由级中间件除了全局中间件还可以给某个路由或某组路由单独加中间件。给单个路由加router.get(/projects,authMiddleware,controller.list);给一组路由加constrouterRouter();// 这些不需要登录router.post(/login,authController.login);router.post(/register,authController.register);// 下面的都需要登录router.use(authMiddleware);router.get(/projects,controller.list);router.get(/projects/:id,controller.get);给子路由加// 管理后台的路由都需要管理员权限router.use(/admin,requireAdmin,createAdminRouter(adminController));路由级中间件的执行顺序是先执行全局的再依次执行路由上挂载的。9.6 路由参数的类型req.params里的参数都是字符串哪怕你定义的是数字 ID。router.get(/projects/:id,(req,res){console.log(typeofreq.params.id);// string// 用的时候要转成数字constidNumber(req.params.id);});这也是为什么我们在 Controller 里用Number(req.params.id)转一下。9.7 常见的路由组织问题❌ 把业务逻辑写在路由文件里// 反面例子router.get(/projects,async(req,res){// 写了一堆业务逻辑constdbawaitgetDB();constlistawaitdb.query(SELECT * FROM projects);res.json({status:0,data:list});});路由文件只负责 URL 映射业务逻辑放在 Controller 和 Service。❌ 路由层级太深/api/v1/admin/settings/notification/email/template/list层级太多不便于理解和查找。一般控制在 3-4 层以内比较合适。❌ 同一个接口有多种写法// 一会用 get一会用 post参数位置也不统一router.get(/project/list,...);router.post(/project/add,...);router.get(/project/detail/:id,...);保持风格一致要么都 RESTful要么都用动词后缀。9.8 小结Router 层负责 URL 到 Controller 的映射按版本 按模块组织路由结构清晰路由文件只写映射关系不写业务逻辑Controller 可以在 bootstrap 集中组装也可以在各路由里分散组装路由级中间件用于给部分接口加特定功能如鉴权、限流