目录结构
project/
│
├── app.js
├── routes/
│ ├── productRoutes.js
│ ├── orderRoutes.js
│ ├── userRoutes.js
│
├── middlewares/
│ ├── authMiddleware.js
│ ├── errorMiddleware.js
│ ├── loggerMiddleware.js
│
├── controllers/
│ ├── productController.js
│ ├── orderController.js
│ ├── userController.js
│
├── models/
│ ├── productModel.js
│ ├── orderModel.js
│ ├── userModel.js
│
├── config/
│ ├── database.js
│ ├── config.js
│
└── public/
app.js
: 主应用文件,用于初始化 Express 应用,引入中间件、路由等。
routes
: 存放各个模块的路由文件,将不同功能的路由分离。
middlewares
: 包含各种中间件,如身份验证、错误处理、日志记录等。
controllers
: 处理业务逻辑,调用模型和数据库操作,响应请求。
models
: 定义数据库模型,与数据库进行交互。
config
: 配置文件,如数据库连接配置、应用配置等。
public
: 存放静态资源,如图片、CSS、JavaScript 等。
中间件分层
- 全局中间件:
- 日志记录中间件:在
middlewares/loggerMiddleware.js
中定义,用于记录每个请求的基本信息,如请求方法、URL 等。
const logger = (req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
};
module.exports = logger;
- 错误处理中间件:在
middlewares/errorMiddleware.js
中定义,捕获整个应用中未处理的异常并返回合适的错误响应。
const errorHandler = (err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something went wrong!');
};
module.exports = errorHandler;
- 路由特定中间件:
- 身份验证中间件:在
middlewares/authMiddleware.js
中定义,用于验证用户登录状态和 Token。例如,对于用户登录后才能访问的路由,如订单创建和某些商品查询(可能涉及用户特定信息)。
const jwt = require('jsonwebtoken');
const auth = (req, res, next) => {
const token = req.headers['authorization'];
if (!token) return res.status(401).send('Access denied. No token provided.');
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (err) {
res.status(400).send('Invalid token.');
}
};
module.exports = auth;
错误处理机制
- 同步错误处理:在控制器函数中,如果发生同步错误,直接调用
next(err)
将错误传递给错误处理中间件。
// controllers/productController.js
const Product = require('../models/productModel');
const getProduct = async (req, res, next) => {
try {
const product = await Product.findById(req.params.id);
if (!product) {
throw new Error('Product not found');
}
res.send(product);
} catch (err) {
next(err);
}
};
module.exports = { getProduct };
- 异步错误处理:对于异步操作,使用
try - catch
块捕获错误并传递给 next
函数。同时,在 app.js
中注册错误处理中间件,确保所有未处理的错误都能被捕获。
// app.js
const express = require('express');
const app = express();
const logger = require('./middlewares/loggerMiddleware');
const errorHandler = require('./middlewares/errorMiddleware');
const productRoutes = require('./routes/productRoutes');
const orderRoutes = require('./routes/orderRoutes');
const userRoutes = require('./routes/userRoutes');
app.use(logger);
app.use(express.json());
app.use('/products', productRoutes);
app.use('/orders', orderRoutes);
app.use('/users', userRoutes);
app.use(errorHandler);
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
确保高效性、稳定性和可扩展性的设计思路
- 高效性:
- 路由优化:将不同功能的路由分离,减少单个路由文件的复杂度,提高路由匹配速度。例如,在
routes
目录下分别定义 productRoutes.js
、orderRoutes.js
和 userRoutes.js
。
- 数据库查询优化:在模型层对数据库查询进行优化,例如使用索引。在
models/productModel.js
中,可以为常用查询字段添加索引。
const mongoose = require('mongoose');
const productSchema = new mongoose.Schema({
name: String,
price: Number
});
productSchema.index({ name: 1 }); // 为name字段添加索引
const Product = mongoose.model('Product', productSchema);
module.exports = Product;
- 稳定性:
- 错误处理:完善的错误处理机制,包括全局错误处理中间件和控制器内的错误捕获,确保应用不会因为未处理的错误而崩溃。
- 中间件验证:使用中间件进行输入验证、身份验证等,确保请求的合法性,减少异常情况的发生。
- 可扩展性:
- 模块化设计:通过目录结构的设计,将应用划分为不同的模块,如路由、控制器、模型等,便于添加新功能或修改现有功能。例如,添加新的商品属性时,只需在
models/productModel.js
和相关的控制器、路由中进行修改。
- 微服务架构:随着业务的增长,可以将各个模块进一步拆分为微服务,实现独立部署和扩展。例如,将订单服务、用户服务、商品服务分别部署,通过 API 进行通信。
关键代码片段汇总
app.js
:
const express = require('express');
const app = express();
const logger = require('./middlewares/loggerMiddleware');
const errorHandler = require('./middlewares/errorMiddleware');
const productRoutes = require('./routes/productRoutes');
const orderRoutes = require('./routes/orderRoutes');
const userRoutes = require('./routes/userRoutes');
app.use(logger);
app.use(express.json());
app.use('/products', productRoutes);
app.use('/orders', orderRoutes);
app.use('/users', userRoutes);
app.use(errorHandler);
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
routes/productRoutes.js
:
const express = require('express');
const router = express.Router();
const { getProduct } = require('../controllers/productController');
router.get('/:id', getProduct);
module.exports = router;
controllers/productController.js
:
const Product = require('../models/productModel');
const getProduct = async (req, res, next) => {
try {
const product = await Product.findById(req.params.id);
if (!product) {
throw new Error('Product not found');
}
res.send(product);
} catch (err) {
next(err);
}
};
module.exports = { getProduct };
models/productModel.js
:
const mongoose = require('mongoose');
const productSchema = new mongoose.Schema({
name: String,
price: Number
});
productSchema.index({ name: 1 });
const Product = mongoose.model('Product', productSchema);
module.exports = Product;
middlewares/loggerMiddleware.js
:
const logger = (req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
};
module.exports = logger;
middlewares/errorMiddleware.js
:
const errorHandler = (err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something went wrong!');
};
module.exports = errorHandler;
middlewares/authMiddleware.js
:
const jwt = require('jsonwebtoken');
const auth = (req, res, next) => {
const token = req.headers['authorization'];
if (!token) return res.status(401).send('Access denied. No token provided.');
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (err) {
res.status(400).send('Invalid token.');
}
};
module.exports = auth;