如何在路由分离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
相关产品推荐
相关产品推荐

