您现在的位置是:网站首页 > 中间件的部署与配置管理文章详情
中间件的部署与配置管理
陈川
【
Node.js
】
12093人已围观
9382字
中间件的概念与作用
Express中间件本质上是函数,能够访问请求对象(req)、响应对象(res)和应用程序的请求-响应周期中的下一个中间件函数。中间件可以执行以下操作:
- 修改请求和响应对象
- 结束请求-响应周期
- 调用堆栈中的下一个中间件
// 最简单的中间件示例
app.use((req, res, next) => {
console.log('请求时间:', new Date());
next();
});
中间件类型
应用级中间件
通过app.use()和app.METHOD()函数绑定到app对象:
// 对所有请求生效
app.use((req, res, next) => {
console.log('应用级中间件');
next();
});
// 只对特定路由生效
app.use('/api', (req, res, next) => {
console.log('API路由中间件');
next();
});
路由级中间件
工作方式与应用级中间件类似,但绑定到express.Router()实例:
const router = express.Router();
router.use((req, res, next) => {
console.log('路由级中间件');
next();
});
错误处理中间件
专门处理错误的中间件,需要四个参数:
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('服务器错误!');
});
内置中间件
Express提供的内置中间件:
- express.static: 托管静态文件
- express.json: 解析JSON请求体
- express.urlencoded: 解析URL编码请求体
app.use(express.static('public'));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
第三方中间件
社区提供的中间件,需要单独安装:
const morgan = require('morgan');
const helmet = require('helmet');
app.use(morgan('dev')); // 日志记录
app.use(helmet()); // 安全相关HTTP头
中间件执行顺序
中间件的执行顺序非常重要,代码中定义的顺序就是执行的顺序:
app.use((req, res, next) => {
console.log('第一个中间件');
next();
});
app.use((req, res, next) => {
console.log('第二个中间件');
next();
});
app.get('/', (req, res) => {
res.send('Hello World');
});
中间件配置管理
环境相关配置
根据环境变量配置不同的中间件:
if (process.env.NODE_ENV === 'development') {
app.use(require('morgan')('dev'));
}
if (process.env.NODE_ENV === 'production') {
app.use(require('compression')());
}
条件中间件
根据条件动态启用中间件:
const unless = (path, middleware) => {
return (req, res, next) => {
if (path === req.path) {
return next();
} else {
return middleware(req, res, next);
}
};
};
app.use(unless('/login', require('authenticate')));
中间件工厂模式
创建可配置的中间件:
function createLogger(format) {
return (req, res, next) => {
console.log(`[${format}] ${req.method} ${req.url}`);
next();
};
}
app.use(createLogger('dev'));
常用中间件配置示例
跨域配置
const cors = require('cors');
// 基本配置
app.use(cors());
// 自定义配置
app.use(cors({
origin: 'https://example.com',
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type']
}));
请求体解析
// JSON解析
app.use(express.json({
limit: '1mb', // 限制请求体大小
strict: true, // 只接受数组和对象
type: 'application/json' // 指定Content-Type
}));
// URL编码解析
app.use(express.urlencoded({
extended: true, // 使用qs库解析
parameterLimit: 1000, // 限制参数数量
limit: '1mb' // 限制请求体大小
}));
会话管理
const session = require('express-session');
app.use(session({
secret: 'your-secret-key',
resave: false,
saveUninitialized: true,
cookie: {
secure: process.env.NODE_ENV === 'production',
maxAge: 24 * 60 * 60 * 1000 // 24小时
}
}));
静态文件服务
app.use(express.static('public', {
dotfiles: 'ignore', // 忽略点文件
etag: true, // 启用ETag
extensions: ['html'], // 自动补全扩展名
index: 'index.html', // 默认索引文件
maxAge: '1d', // 缓存时间
redirect: true // 重定向到目录
}));
中间件性能优化
中间件缓存
const apicache = require('apicache');
let cache = apicache.middleware;
// 缓存所有GET请求5分钟
app.use(cache('5 minutes'));
// 特定路由缓存
app.get('/api/data', cache('10 minutes'), (req, res) => {
// 数据处理逻辑
});
中间件压缩
const compression = require('compression');
app.use(compression({
level: 6, // 压缩级别(1-9)
threshold: '1kb', // 最小压缩大小
filter: (req, res) => {
if (req.headers['x-no-compression']) {
return false;
}
return compression.filter(req, res);
}
}));
中间件并行处理
const { parallelMiddlewares } = require('express-parallel-middleware');
const middleware1 = (req, res, next) => { /* ... */ };
const middleware2 = (req, res, next) => { /* ... */ };
app.use(parallelMiddlewares([middleware1, middleware2]));
中间件错误处理最佳实践
统一错误处理
// 业务逻辑中间件
app.get('/user/:id', async (req, res, next) => {
try {
const user = await getUserById(req.params.id);
res.json(user);
} catch (err) {
next(err); // 传递到错误处理中间件
}
});
// 错误处理中间件
app.use((err, req, res, next) => {
console.error(err);
const statusCode = err.statusCode || 500;
const message = statusCode === 500 ? '服务器错误' : err.message;
res.status(statusCode).json({
error: {
message: message,
code: statusCode
}
});
});
异步错误处理
// 包装异步中间件
const asyncHandler = fn => (req, res, next) => {
Promise.resolve(fn(req, res, next))
.catch(next);
};
// 使用示例
app.get('/async', asyncHandler(async (req, res) => {
const data = await fetchData();
res.json(data);
}));
404处理
// 放在所有路由之后
app.use((req, res, next) => {
res.status(404).json({
error: {
message: '未找到资源',
code: 404
}
});
});
中间件测试与调试
单元测试中间件
const request = require('supertest');
const express = require('express');
describe('认证中间件', () => {
let app;
beforeEach(() => {
app = express();
app.use(require('./authMiddleware'));
app.get('/', (req, res) => res.send('OK'));
});
it('应该拒绝没有token的请求', async () => {
const res = await request(app).get('/');
expect(res.statusCode).toBe(401);
});
});
中间件调试技巧
// 调试中间件执行顺序
app.use((req, res, next) => {
console.log('中间件1 - 开始');
next();
console.log('中间件1 - 结束');
});
app.use((req, res, next) => {
console.log('中间件2 - 开始');
next();
console.log('中间件2 - 结束');
});
// 使用debug模块
const debug = require('debug')('app:middleware');
app.use((req, res, next) => {
debug('请求 %s %s', req.method, req.url);
next();
});
自定义中间件开发
请求计时中间件
function responseTime() {
return (req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`${req.method} ${req.url} - ${duration}ms`);
});
next();
};
}
app.use(responseTime());
API版本控制中间件
function versionMiddleware(versions) {
return (req, res, next) => {
const version = req.headers['accept-version'] || 'v1';
if (!versions[version]) {
return res.status(400).json({
error: `不支持的API版本: ${version}`
});
}
req.version = version;
req.versionHandler = versions[version];
next();
};
}
app.use(versionMiddleware({
v1: require('./controllers/v1'),
v2: require('./controllers/v2')
}));
请求验证中间件
function validate(schema) {
return (req, res, next) => {
const { error } = schema.validate(req.body);
if (error) {
return res.status(400).json({
error: error.details[0].message
});
}
next();
};
}
const Joi = require('joi');
const userSchema = Joi.object({
username: Joi.string().alphanum().min(3).max(30).required(),
password: Joi.string().pattern(new RegExp('^[a-zA-Z0-9]{3,30}$'))
});
app.post('/users', validate(userSchema), (req, res) => {
// 处理有效请求
});
中间件安全实践
安全头部设置
const helmet = require('helmet');
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", 'cdn.example.com'],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", 'data:', 'cdn.example.com']
}
}));
app.use(helmet.referrerPolicy({ policy: 'same-origin' }));
速率限制
const rateLimit = require('express-rate-limit');
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100, // 每个IP限制100个请求
message: '请求过多,请稍后再试'
});
app.use('/api/', apiLimiter);
CSRF保护
const csrf = require('csurf');
const cookieParser = require('cookie-parser');
app.use(cookieParser());
app.use(csrf({ cookie: true }));
// 在视图中使用CSRF令牌
app.use((req, res, next) => {
res.locals.csrfToken = req.csrfToken();
next();
});
中间件与微服务架构
API网关中间件
const { createProxyMiddleware } = require('http-proxy-middleware');
app.use('/users', createProxyMiddleware({
target: 'http://user-service:3000',
changeOrigin: true,
pathRewrite: { '^/users': '' }
}));
app.use('/products', createProxyMiddleware({
target: 'http://product-service:3000',
changeOrigin: true,
pathRewrite: { '^/products': '' }
}));
服务间认证中间件
function serviceAuth(allowedServices) {
return (req, res, next) => {
const serviceToken = req.headers['x-service-token'];
if (!serviceToken || !allowedServices.includes(serviceToken)) {
return res.status(403).json({
error: '服务间认证失败'
});
}
next();
};
}
app.use('/internal', serviceAuth(['AUTH_SERVICE', 'PAYMENT_SERVICE']));
请求追踪中间件
const { v4: uuidv4 } = require('uuid');
app.use((req, res, next) => {
req.requestId = uuidv4();
res.setHeader('X-Request-ID', req.requestId);
// 传递给下游服务
if (req.headers['x-service-call']) {
req.headers['x-request-id'] = req.requestId;
}
next();
});
上一篇: 中间件的版本兼容性问题
下一篇: 中间件的最佳实践总结