You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Express项目路由拆分至不同模块,无需重写代码?

无需重写代码的路由拆分方案

完全可以通过Express内置的express.Router()实现路由拆分,不需要修改现有路由的业务逻辑,仅需迁移代码并完成挂载即可。以下是具体步骤:

1. 抽离共享资源

先把路由中需要复用的中间件、模型、配置单独拆分,避免重复定义:

1.1 抽离认证中间件

创建middleware/auth.js,把authenticateToken和isAdmin移到这里:

const jwt = require('jsonwebtoken');
const db = require('../db');

const authenticateToken = (req, res, next) => {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];
  
  console.log('Token received:', token);

  if (token == null) return res.sendStatus(401);

  jwt.verify(token, 'your_secret_key_here', (err, user) => {
    if (err) {
      console.error('Token verification failed:', err);
      return res.sendStatus(403);
    }
    req.user = user;
    console.log('User from token:', user);
    next();
  });
};

const isAdmin = async (req, res, next) => {
  try {
    const user = await db.User.findByPk(req.user.id);
    if (user && user.RoleID === 2) {
      next();
    } else {
      res.sendStatus(403);
    }
  } catch (err) {
    res.sendStatus(500);
  }
};

module.exports = { authenticateToken, isAdmin };

1.2 抽离文件上传配置

创建middleware/upload.js,把multer配置移到这里:

const multer = require('multer');
const path = require('path');

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'uploads/');
  },
  filename: function (req, file, cb) {
    cb(null, `${Date.now()}-${file.originalname}`);
  }
});
const upload = multer({ storage: storage });

module.exports = upload;

1.3 抽离Sequelize实例和模型

创建config/sequelize.js,单独存放数据库连接实例:

const { Sequelize } = require('sequelize');

const sequelize = new Sequelize('school_canteen', 'root', 'password', {
  host: 'localhost',
  dialect: 'mysql'
});

module.exports = sequelize;

创建models/Photo.js,把Photo模型移到这里:

const { Sequelize, DataTypes } = require('sequelize');
const sequelize = require('../config/sequelize');

const Photo = sequelize.define('Photo', {
  PhotoID: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true },
  photoUrl: { type: Sequelize.STRING, allowNull: false },
  productId: { type: Sequelize.INTEGER, allowNull: false },
  altText: { type: Sequelize.STRING, allowNull: true }
}, {
  tableName: 'photos'
});

module.exports = Photo;

2. 创建独立路由文件

创建routes/photos.js,把原server.js中照片相关的路由全部迁移过来,用router替代app:

const express = require('express');
const router = express.Router();
const Photo = require('../models/Photo');
const { authenticateToken, isAdmin } = require('../middleware/auth');
const upload = require('../middleware/upload');

// 新增照片(管理员)
router.post('/admin/photos', authenticateToken, isAdmin, upload.single('photo'), async (req, res) => {
  const { productId, altText } = req.body;
  const photoUrl = req.file ? req.file.path : undefined;

  console.log('Request Body:', req.body);
  console.log('Uploaded File:', req.file);

  if (!productId || !photoUrl) {
    return res.status(400).json({ error: 'ProductID and PhotoURL are required' });
  }

  try {
    const newPhoto = await Photo.create({
      photoUrl,
      productId,
      altText
    });
    res.status(201).json(newPhoto);
  } catch (err) {
    console.error('Error creating photo:', err);
    res.status(500).send('Server Error');
  }
});

// 更新照片(管理员)
router.put('/admin/photos/:photoId', authenticateToken, isAdmin, upload.single('photo'), async (req, res) => {
  const { photoId } = req.params;
  const { productId, altText } = req.body;
  const photoUrl = req.file ? req.file.path : undefined;

  try {
    const photo = await Photo.findByPk(photoId);
    if (!photo) return res.status(404).json({ msg: 'Photo not found' });

    photo.productId = productId || photo.productId;
    photo.altText = altText || photo.altText;
    if (photoUrl) { 
      photo.photoUrl = photoUrl;
    }
    await photo.save();
    res.json(photo);
  } catch (err) {
    console.error(err.message);
    res.status(500).send('Server Error');
  }
});

// 删除照片
router.delete('/admin/photos/:photoId', authenticateToken, async (req, res) => {
  const { photoId } = req.params;

  try {
    const photo = await Photo.findByPk(photoId);
    if (!photo) {
      return res.status(404).json({ error: 'Photo not found' });
    }

    await photo.destroy();
    res.status(200).json({ message: 'Photo deleted successfully' });
  } catch (error) {
    console.error('Error deleting photo:', error);
    res.status(500).json({ error: 'Failed to delete photo' });
  }
});

// 获取所有照片
router.get('/photos', authenticateToken, async (req, res) => {
  try {
    const photos = await Photo.findAll();
    res.json(photos);
  } catch (error) {
    console.error('Error fetching photos:', error);
    res.status(500).json({ error: 'Failed to fetch photos' });
  }
});

// 根据产品ID获取照片
router.get('/photos/:productId', authenticateToken, async (req, res) => {
  try {
    const photos = await Photo.findAll({ where: { productId: req.params.productId } });
    res.json(photos);
  } catch (err) {
    console.error('Error fetching photos:', err.message);
    res.status(500).send('Server Error');
  }
});

module.exports = router;

3. 简化server.js并挂载路由

修改原server.js,删除所有照片路由代码,替换为路由挂载:

const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const path = require('path');
const sequelize = require('./config/sequelize');
const db = require('./db');
// 引入拆分后的路由
const photosRoutes = require('./routes/photos');

const app = express();
const PORT = process.env.PORT || 3001;

app.use(cors());
app.use(bodyParser.json());
app.use('/uploads', express.static(path.join(__dirname, 'uploads')));

// 挂载照片路由,前缀为/api
app.use('/api', photosRoutes);

sequelize.sync().then(() => {
  console.log('Database synchronized');
}).catch(error => {
  console.error('Error synchronizing the database:', error);
});

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

后续扩展

其他路由(如用户、订单、菜单等)都可以按照相同逻辑拆分:

  • 新建对应路由文件(如routes/users.js、routes/orders.js)
  • 迁移对应路由代码到文件中
  • 在server.js中引入并挂载

内容的提问来源于stack exchange,提问作者V4lis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 05:40:54