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

如何在路由分离MVC架构Express.js应用中集成MongoDB等常用组件

问题背景

我正在将一个约8000行的单文件应用重构为「路由分离」的MVC风格Express.js项目,希望对文件夹/文件结构和app.js的内容应用最佳实践,保持整体架构简单、直观、轻量化。
我参考了多个学习资源提取了最合理的实现部分,已经完成了90%的重构工作,但未覆盖以下需求的处理方案:

  • MongoDB client配置
  • Socket.io实现
  • app.use()全局请求中间件配置
  • JWT令牌校验
  • Simple template engine实现
    这些都是非常常见的功能,我希望能有标准的方案来处理这些需求,也希望这个问题能对其他有同类需求的开发者有参考价值。
现有运行正常的实现

目录结构

app.js <---- 入口文件
routes
- index.js
- resource_01_route.js
- resource_02_route.js
- resource_03_route.js
// 其他路由
middleware
- middleware_01.js
- middleware_02.js
- middleware_03.js
// 其他中间件
views
- index.ntl
config
- db_connection.js
- environment_variables.js
- local_settings.js

routes/index.js

const express = require('express');
const router = express.Router();

const resource_01_route = require('./resource_01_route');
const resource_02_route = require('./resource_02_route');
const resource_03_route = require('./resource_03_route');

router.use('/api/:api_version/resource_01', resource_01_route);
router.use('/api/:api_version/resource_02', resource_02_route);
router.use('/api/:api_version/resource_03', resource_03_route);

module.exports = router;

routes/resource_01_route.js

const express = require('express');
const router = express.Router();

const middleware_01 = require('../middleware/middleware_01');
const middleware_02 = require('../middleware/middleware_02');
const middleware_03 = require('../middleware/middleware_03');

router
    .route('/')
    .get(middleware_01)
    .post(middleware_02)
    .delete(middleware_03);

// 其他路由逻辑

module.exports = router;

middleware/middleware_01.js

const { db_connection } = require('../config/db_connection');
db_connection.open();

const middleware_01 = async (req, res) => {
    try {
        const query = { username: 'my name' };
        const collection = db_connection.conn.db('users').collection('users');
        const result = await collection.findOne(query);
        res.json({ result: result });
    } catch (error) {
        console.log(error);
        res.status(500).json({ error });
    }
};

module.exports = middleware_01;

config/db_connection.js

const MongoClient = require('mongodb').MongoClient;
const { connection_string } = require('./environment_variables');

class db_connection {
  static async open() {
    if (this.conn) return this.conn;
    this.conn = await MongoClient.connect(connection_string);
    return this.conn;
  }
}

db_connection.conn = null;
db_connection.url = connection_string;

module.exports = { db_connection };

config/environment_variables.js

let online_status,
    PORT,
    var_01,
    var_02,
    var_03,
    var_04; // 其他变量

if (process.env.MONGODB_USER) {
    online_status = 'online';
} else {
    online_status = 'offline';
    local_settings = require('./local_settings');
}

if (online_status === 'online') {
    PORT = 8080;
    var_01 = process.env.VAR_01;
    var_02 = process.env.VAR_02;
    var_03 = process.env.VAR_03;
    var_04 = process.env.VAR_04;
} else if (online_status === 'offline') {
    PORT = 3000;
    var_01 = local_settings.VAR_01;
    var_02 = local_settings.VAR_02;
    var_03 = local_settings.VAR_03;
    var_04 = local_settings.VAR_04;
}

module.exports = {
    online_status,
    PORT,
    var_01,
    var_02,
    var_03,
    var_04,
};

config/local_settings.js(已被.gitignore忽略)

module.exports = {
    VAR_01: 'VAR_01',
    VAR_02: 'VAR_02',
    VAR_03: 'VAR_03',
    VAR_04: 'VAR_04',
};
期望的app.js简洁形态
const express = require('express');
const app = express();
const routes = require('./routes');

app.use('/', routes); // <---- 引入routes/index.js文件

app.listen(PORT, () => {
    console.log(`the app is listening`);
});
待处理的功能点

我已经基本将路由和中间件的管理逻辑从app.js中剥离,但app.js中还有大量剩余的功能代码不知道如何处理(要么集成到app.js中,要么拆分到其他文件保证app.js的简洁),这些待处理的内容包括:

  • 多中间件共享的mongo_client引用 解决方案已找到,如上所示
  • Socket.io逻辑,示例如下:
io.on('connection', async (socket) => {
/*
定义大量socket.io逻辑
需要访问mongo_client
*/
});
  • 全请求生效的app.use()中间件,示例如下:
app.use(
  helmet({
    contentSecurityPolicy: {
      useDefaults: true,
      directives: {
        defaultSrc: ["'self'"],
        scriptSrc: ["'self'", 'https://static.codepen.io'],
        styleSrc: ["'self'", 'fonts.googleapis.com', "'unsafe-inline'"],
        fontSrc: ["'self'", 'fonts.gstatic.com'],
        frameSrc: ["'self'", 'https://codepen.io'],
      },
    },
  })
);

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(express.static('dist'));
  • JWT校验逻辑,示例如下:
app.use((req, res, next) => {
/*
定义所有JWT校验逻辑
需要访问mongo_client
*/
next();
});
  • Simple template engine定义和res.render配置,示例如下:
app.engine('ntl', (filePath, options, callback) => {
  fs.readFile(filePath, (err, content) => {
    if (err) return callback(err);
    var rendered = content
      .toString()
      .replace('#page_html#', options.page_html)
      .replace(/#site_title#/g, site_title)
      .replace(/#site_description#/g, site_description)
      .replace(/#og_image#/g, og_image)
      .replace(/#og_url#/g, og_url);
    return callback(null, rendered);
  });
});

app.set('views', './views');
app.set('view engine', 'ntl');
当前完整待重构的app.js代码
import express from 'express';
import cors from 'cors';
import bodyParser from 'body-parser';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';

// 模板引擎依赖
const fs = require('fs');

// 安全依赖
const helmet = require('helmet');

// socket.io依赖
const app = express();
const http = require('http');
const server = http.createServer(app);
const io = require('socket.io')(server);
app.set('socketio', io);

// 暂时忘记用途
const path = require('path');

io.on('connection', async (socket) => {
/*
定义大量socket.io逻辑
需要访问mongo_client
 */
});

app.use(
  helmet({
    contentSecurityPolicy: {
      useDefaults: true,
      directives: {
        defaultSrc: ["'self'"],
        scriptSrc: ["'self'", 'https://static.codepen.io'],
        styleSrc: ["'self'", 'fonts.googleapis.com', "'unsafe-inline'"],
        fontSrc: ["'self'", 'fonts.gstatic.com'],
        frameSrc: ["'self'", 'https://codepen.io'],
      },
    },
  })
);


app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(express.static('dist'));

app.use((req, res, next) => {
/*
定义所有JWT校验逻辑
需要访问mongo_client
 */
next();
});

// 简单模板引擎开始
app.engine('ntl', (filePath, options, callback) => {
  fs.readFile(filePath, (err, content) => {
    if (err) return callback(err);
    var rendered = content
      .toString()
      .replace('#page_html#', options.page_html)
      .replace(/#site_title#/g, site_title)
      .replace(/#site_description#/g, site_description)
      .replace(/#og_image#/g, og_image)
      .replace(/#og_url#/g, og_url);
    return callback(null, rendered);
  });
});

app.set('views', './views');
app.set('view engine', 'ntl');

// 简单模板引擎结束

server.listen(PORT, () => {
  console.log(`running on port ${PORT}`);
});
问题

请问上述待处理的功能该如何拆分或集成,才能保证架构合理、app.js保持简洁?


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 16:24:02