09 — Router 层:路由配置
2026/9/24 17:28:42 网站建设 项目流程

9.1 Router 层是什么

Router 层负责把 URL 和 HTTP 方法映射到对应的 Controller 方法

Express 的Router可以把路由分组管理,而不是把所有路由都堆在app.ts里。


9.2 基本用法

最简单的写法

import{Router}from'express';import{ProjectController}from'@/controllers/project/project-controller.js';exportfunctioncreateProjectRouter(controller:ProjectController){constrouter=Router();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){constrouter=Router();// 所有路由自动带 /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.ts

import{Router}from'express';import{createApiRouter}from'./api-router.js';exportfunctioncreateRouter(){constrouter=Router();// API 路由router.use('/api',createApiRouter());// 健康检查(不放在 /api 下也行)router.get('/health',(_req,res)=>{res.json({status:'ok'});});returnrouter;}

API 总路由:src/routers/api-router.ts

import{Router}from'express';import{createV1Router}from'./v1/index.js';exportfunctioncreateApiRouter(){constrouter=Router();router.use('/v1',createV1Router());// router.use('/v2', createV2Router()); // 以后加 v2 版本returnrouter;}

版本路由:src/routers/v1/index.ts

import{Router}from'express';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){constrouter=Router();// 组装各模块的 Controller 和 ServiceconstprojectController=newProjectController(newProjectService(models.projects),);constuserController=newUserController(newUserService(models.users),);// 挂载各模块路由router.use('/projects',createProjectRouter(projectController));router.use('/users',createUserRouter(userController));returnrouter;}

模块路由:src/routers/project/project-router.ts

import{Router}from'express';importtype{ProjectController}from'@/controllers/project/project-controller.js';exportfunctioncreateProjectRouter(controller:ProjectController){constrouter=Router();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);

给一组路由加

constrouter=Router();// 这些不需要登录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"// 用的时候要转成数字constid=Number(req.params.id);});

这也是为什么我们在 Controller 里用Number(req.params.id)转一下。


9.7 常见的路由组织问题

❌ 把业务逻辑写在路由文件里

// 反面例子router.get('/projects',async(req,res)=>{// 写了一堆业务逻辑constdb=awaitgetDB();constlist=awaitdb.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 集中组装,也可以在各路由里分散组装
  • 路由级中间件用于给部分接口加特定功能(如鉴权、限流)

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询