您现在的位置是:网站首页 > 路由分层与模块化设计文章详情

路由分层与模块化设计

路由分层与模块化设计是构建可维护、可扩展Express应用的关键。合理的分层能够清晰划分职责,模块化则有助于代码复用和团队协作。下面从路由分层策略、模块化实现方式以及实际应用场景展开讨论。

路由分层基础结构

Express应用通常采用三层路由结构:入口路由→业务路由→控制器。基础实现方式如下:

// 入口文件 app.js
const userRouter = require('./routes/users');
app.use('/users', userRouter);

// routes/users.js
const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');

router.get('/', userController.listUsers);
router.post('/', userController.createUser);

// controllers/userController.js
exports.listUsers = (req, res) => {
  // 业务逻辑处理
  res.json([...]);
};

这种分层将URL路径匹配、路由分发和业务处理完全分离。实际项目中可以进一步细化:

  1. 路由层只做路径映射和参数验证
  2. 控制器处理核心业务逻辑
  3. 服务层处理数据操作
  4. 模型层定义数据结构

模块化路由实现

动态路由加载

大型项目需要自动化加载路由模块,避免手动注册每个路由文件:

// utils/routeLoader.js
const fs = require('fs');
const path = require('path');

module.exports = (app) => {
  fs.readdirSync(__dirname)
    .filter(file => file !== 'index.js')
    .forEach(file => {
      const route = require(path.join(__dirname, file));
      app.use(route.prefix, route.router);
    });
};

// routes/users.js
module.exports = {
  prefix: '/api/v1/users',
  router: require('express').Router()
    .get('/', controller.list)
    .post('/', controller.create)
};

版本化路由管理

API版本迭代时可采用路径版本控制:

// app.js
app.use('/api/v1', require('./routes/v1'));
app.use('/api/v2', require('./routes/v2'));

// routes/v1/users.js
router.get('/profile', v1UserController.getProfile);

// routes/v2/users.js 
router.get('/profile', v2UserController.getProfile);

中间件分层设计

中间件也应该按照功能进行分层管理:

// middleware/auth.js
module.exports = {
  basicAuth: (req, res, next) => {
    // 基础认证逻辑
    next();
  },
  adminCheck: (req, res, next) => {
    // 管理员权限检查
    next();
  }
};

// routes/admin.js
const { basicAuth, adminCheck } = require('../middleware/auth');
router.use(basicAuth, adminCheck);

路由参数规范化

统一处理路由参数可以提高代码一致性:

// middleware/params.js
module.exports = {
  validateUserId: (req, res, next) => {
    if (!isValidId(req.params.id)) {
      return res.status(400).json({ error: 'Invalid ID' });
    }
    next();
  }
};

// routes/users.js
router.get('/:id', 
  params.validateUserId,
  controller.getUser
);

微服务架构下的路由设计

在微服务架构中,Express路由可以作为API网关:

// gateway/routes/products.js
const axios = require('axios');

router.get('/products/:id', async (req, res) => {
  try {
    const response = await axios.get(
      `http://product-service/${req.params.id}`
    );
    res.json(response.data);
  } catch (err) {
    res.status(502).json({ error: 'Service unavailable' });
  }
});

性能优化策略

路由设计直接影响性能,需要注意:

  1. 避免在路由层进行复杂计算
  2. 高频路由路径应该尽量短
  3. 合理使用路由缓存
// 使用route cache
const apicache = require('apicache');
const cache = apicache.middleware;

router.get('/popular-items', 
  cache('10 minutes'),
  controller.getPopularItems
);

测试策略

路由应该单独进行测试,与业务逻辑分离:

// test/routes/users.test.js
const request = require('supertest');
const app = require('../../app');

describe('GET /users', () => {
  it('should return 200', async () => {
    const res = await request(app)
      .get('/api/v1/users')
      .expect(200);
    expect(Array.isArray(res.body)).toBeTruthy();
  });
});

错误处理标准化

统一错误处理中间件应该放在路由最后:

// middleware/errorHandler.js
module.exports = (err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ 
    code: err.code || 'UNKNOWN_ERROR',
    message: err.message 
  });
};

// app.js
app.use('/api', apiRouter);
app.use(errorHandler); // 最后加载

路由元数据管理

为路由添加元数据便于生成文档:

// decorators/routeMeta.js
module.exports = (meta) => {
  return (target, key, descriptor) => {
    descriptor.value.__routeMeta = meta;
    return descriptor;
  };
};

// controllers/userController.js
class UserController {
  @routeMeta({
    description: 'Get user list',
    params: ['page', 'limit']
  })
  static async listUsers(req, res) {
    // ...
  }
}

前端路由与服务端路由协同

在前后端分离项目中保持路由一致性:

// shared/routes.js
module.exports = {
  user: {
    profile: (id) => `/users/${id}/profile`,
    update: (id) => `/users/${id}`
  }
};

// 服务端路由
router.get(sharedRoutes.user.profile(':id'), controller.getProfile);

// 前端请求
axios.get(sharedRoutes.user.profile(123));

路由配置中心化

将路由配置集中管理:

// config/routes.js
module.exports = [
  {
    path: '/users',
    method: 'get',
    handler: 'userController.list',
    middleware: ['auth']
  },
  // 其他路由配置...
];

// utils/routeBuilder.js
routesConfig.forEach(config => {
  const middlewares = config.middleware
    .map(m => require(`../middleware/${m}`));
  
  app[config.method](
    config.path,
    ...middlewares,
    require(`../controllers/${config.handler}`)
  );
});

动态路由权限控制

基于角色的动态路由权限管理:

// middleware/dynamicAuth.js
module.exports = (requiredRole) => {
  return (req, res, next) => {
    if (req.user.role !== requiredRole) {
      return res.sendStatus(403);
    }
    next();
  };
};

// routes/admin.js
router.get('/dashboard', 
  dynamicAuth('admin'),
  adminController.dashboard
);

路由性能监控

添加路由性能追踪:

// middleware/performance.js
module.exports = (req, res, next) => {
  const start = process.hrtime();
  
  res.on('finish', () => {
    const diff = process.hrtime(start);
    const ms = diff[0] * 1e3 + diff[1] * 1e-6;
    console.log(`${req.method} ${req.path} took ${ms.toFixed(2)}ms`);
  });
  
  next();
};

// app.js
app.use(performanceMiddleware);

路由版本迁移策略

处理API版本迁移时的路由兼容:

// middleware/versionRedirect.js
module.exports = (options) => {
  return (req, res, next) => {
    if (req.headers['x-api-version'] === '1.0') {
      return res.redirect(301, `/v1${req.path}`);
    }
    next();
  };
};

// app.js
app.use(versionRedirect());
app.use('/v2', v2Router);

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步