中间件的部署与配置管理
中间件的概念与作用
Express中间件本质上是函数,能够访问请求对象(req)、响应对象(res)和应用程序的请求-响应周期中的下一个中间件函数。这些函数可以执行以下操作:
- 修改请求和响应对象
- 结束请求-响应周期
- 调用堆栈中的下一个中间件
// 最简单的中间件示例
app.use((req, res, next) => {
console.log('Time:', Date.now());
next();
});
中间件的类型
Express中的中间件主要分为以下几种类型:
- 应用级中间件
// 使用app.use()加载的应用级中间件
app.use('/user/:id', (req, res, next) => {
console.log('Request Type:', req.method);
next();
});
- 路由级中间件
// 路由特定的中间件
router.get('/user/:id', (req, res, next) => {
console.log('Request URL:', req.originalUrl);
next();
}, (req, res) => {
res.send('User Info');
});
- 错误处理中间件
// 错误处理中间件需要四个参数
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
- 内置中间件
// Express内置的静态文件中间件
app.use(express.static('public'));
- 第三方中间件
// 常用的第三方中间件示例
const bodyParser = require('body-parser');
app.use(bodyParser.json());
中间件的加载顺序
中间件的执行顺序至关重要,Express会按照中间件加载的顺序依次执行:
// 第一个中间件
app.use((req, res, next) => {
console.log('First middleware');
next();
});
// 第二个中间件
app.use('/user/:id', (req, res, next) => {
console.log('Request Type:', req.method);
next();
});
// 路由处理程序
app.get('/user/:id', (req, res) => {
res.send('User Info');
});
中间件的配置管理
合理的配置管理可以提高中间件的可维护性:
- 环境变量配置
const morgan = require('morgan');
if (process.env.NODE_ENV === 'development') {
app.use(morgan('dev'));
}
- 配置文件分离
// config/middlewares.js
module.exports = (app) => {
app.use(require('helmet')());
app.use(require('compression')());
};
- 条件加载中间件
const setupMiddlewares = (app) => {
// 基础中间件
app.use(express.json());
// 开发环境专用中间件
if (process.env.NODE_ENV !== 'production') {
app.use(require('morgan')('dev'));
app.use(require('errorhandler')());
}
};
常用中间件的配置示例
- CORS配置
const cors = require('cors');
app.use(cors({
origin: ['https://example.com', 'https://api.example.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
- Helmet安全中间件
const helmet = require('helmet');
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", 'cdn.example.com']
}
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
}
}));
- Rate Limiting
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100, // 每个IP限制100个请求
message: 'Too many requests from this IP, please try again later'
});
app.use('/api/', limiter);
自定义中间件开发
开发自定义中间件可以满足特定业务需求:
- 请求日志中间件
function requestLogger(format) {
return (req, res, next) => {
const log = format
.replace(':method', req.method)
.replace(':url', req.url)
.replace(':date', new Date().toISOString());
console.log(log);
next();
};
}
app.use(requestLogger(':method :url at :date'));
- 认证中间件
function authenticate(req, res, next) {
const authHeader = req.headers['authorization'];
if (!authHeader) return res.sendStatus(401);
const token = authHeader.split(' ')[1];
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
}
app.get('/protected', authenticate, (req, res) => {
res.json({ message: 'Protected data' });
});
- 数据验证中间件
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 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('/register', validate(userSchema), (req, res) => {
// 处理注册逻辑
});
中间件的性能优化
- 中间件缓存
const cache = require('memory-cache');
const memCache = new cache.Cache();
function cacheMiddleware(duration) {
return (req, res, next) => {
const key = '__express__' + req.originalUrl || req.url;
const cacheContent = memCache.get(key);
if (cacheContent) {
res.send(cacheContent);
return;
} else {
res.sendResponse = res.send;
res.send = (body) => {
memCache.put(key, body, duration * 1000);
res.sendResponse(body);
};
next();
}
};
}
app.get('/api/data', cacheMiddleware(30), (req, res) => {
// 从数据库获取数据
res.json({ data: 'Expensive data' });
});
- 中间件并行处理
const { parallelMiddlewares } = require('express-parallel-middleware');
const middleware1 = (req, res, next) => {
setTimeout(() => {
req.data1 = 'Data from middleware 1';
next();
}, 1000);
};
const middleware2 = (req, res, next) => {
setTimeout(() => {
req.data2 = 'Data from middleware 2';
next();
}, 1500);
};
app.use(parallelMiddlewares([middleware1, middleware2]));
app.get('/parallel', (req, res) => {
res.json({
data1: req.data1,
data2: req.data2
});
});
- 中间件懒加载
function lazyLoadMiddleware(modulePath) {
let middleware = null;
return (req, res, next) => {
if (!middleware) {
middleware = require(modulePath);
}
return middleware(req, res, next);
};
}
app.use(lazyLoadMiddleware('./heavy-middleware'));
中间件的测试策略
- 单元测试中间件
const request = require('supertest');
const express = require('express');
describe('Authentication Middleware', () => {
let app;
beforeEach(() => {
app = express();
app.use(express.json());
app.use(require('./middlewares/auth'));
app.get('/test', (req, res) => res.status(200).send());
});
it('should return 401 without token', async () => {
const res = await request(app).get('/test');
expect(res.statusCode).toEqual(401);
});
it('should return 200 with valid token', async () => {
const token = generateValidToken();
const res = await request(app)
.get('/test')
.set('Authorization', `Bearer ${token}`);
expect(res.statusCode).toEqual(200);
});
});
- 集成测试中间件链
describe('Middleware Chain', () => {
it('should execute in correct order', (done) => {
const app = express();
const order = [];
app.use((req, res, next) => {
order.push(1);
next();
});
app.use((req, res, next) => {
order.push(2);
next();
});
app.get('/', (req, res) => {
order.push(3);
res.send();
expect(order).toEqual([1, 2, 3]);
done();
});
request(app).get('/').end(() => {});
});
});
- 性能测试中间件
const loadTest = require('loadtest');
describe('Rate Limiter Middleware Performance', () => {
it('should handle 100 requests in under 2 seconds', (done) => {
const options = {
url: 'http://localhost:3000/api',
maxRequests: 100,
concurrency: 10,
method: 'GET'
};
loadTest.loadTest(options, (error, results) => {
if (error) return done(error);
expect(results.totalTimeSeconds).toBeLessThan(2);
done();
});
});
});
中间件的错误处理
- 异步错误处理
function asyncHandler(fn) {
return (req, res, next) => {
Promise.resolve(fn(req, res, next))
.catch(next);
};
}
app.get('/async', asyncHandler(async (req, res) => {
const data = await someAsyncOperation();
res.json(data);
}));
- 错误分类处理
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
this.status = 400;
}
}
app.use((err, req, res, next) => {
if (err.name === 'ValidationError') {
return res.status(err.status).json({
error: err.message
});
}
next(err);
});
- 错误日志中间件
function errorLogger(err, req, res, next) {
console.error(`[${new Date().toISOString()}] ${err.stack}`);
next(err);
}
function clientErrorHandler(err, req, res, next) {
if (req.xhr) {
res.status(500).json({ error: 'Something failed!' });
} else {
next(err);
}
}
app.use(errorLogger);
app.use(clientErrorHandler);
中间件的版本管理
- API版本控制中间件
function apiVersion(version) {
return (req, res, next) => {
req.apiVersion = version;
next();
};
}
const v1Router = express.Router();
v1Router.use(apiVersion('v1'));
v1Router.get('/users', (req, res) => {
res.json({ version: req.apiVersion, users: [] });
});
const v2Router = express.Router();
v2Router.use(apiVersion('v2'));
v2Router.get('/users', (req, res) => {
res.json({ version: req.apiVersion, users: [], meta: {} });
});
app.use('/api/v1', v1Router);
app.use('/api/v2', v2Router);
- 中间件版本兼容性检查
function checkCompatibility(minVersion) {
return (req, res, next) => {
const clientVersion = req.headers['x-api-version'];
if (compareVersions(clientVersion, minVersion) < 0) {
return res.status(426).json({
error: 'Upgrade required',
minVersion: minVersion
});
}
next();
};
}
app.use(checkCompatibility('1.2.0'));
中间件的动态加载
- 按需加载中间件
function dynamicLoader(config) {
return (req, res, next) => {
const middlewareName = req.headers['x-middleware'];
if (config[middlewareName]) {
return config[middlewareName](req, res, next);
}
next();
};
}
const middlewares = {
logger: require('./middlewares/logger'),
auth: require('./middlewares/auth')
};
app.use(dynamicLoader(middlewares));
- 功能开关中间件
function featureToggle(features) {
return (req, res, next) => {
req.features = {};
for (const [name, isActive] of Object.entries(features)) {
req.features[name] = isActive;
}
next();
};
}
app.use(featureToggle({
newDashboard: process.env.FEATURE_DASHBOARD === 'true',
experimentalAPI: false
}));
app.get('/dashboard', (req, res) => {
if (req.features.newDashboard) {
return res.send('New Dashboard');
}
res.send('Old Dashboard');
});
中间件的监控与指标
- 请求指标收集
const prometheus = require('prom-client');
const httpRequestDurationMicroseconds = new prometheus.Histogram({
name: 'http_request_duration_ms',
help: 'Duration of HTTP requests in ms',
labelNames: ['method', 'route', 'code'],
buckets: [0.1, 5, 15, 50, 100, 200, 300, 400, 500]
});
function metricsMiddleware(req, res, next) {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
httpRequestDurationMicroseconds
.labels(req.method, req.route.path, res.statusCode)
.observe(duration);
});
next();
}
app.use(metricsMiddleware);
- 健康检查中间件
function healthCheck(options = {}) {
const { path = '/health', checks = [] } = options;
return (req, res, next) => {
if (req.path !== path) return next();
Promise.all(checks.map(check => check()))
.then(results => {
const isHealthy = results.every(result => result.healthy);
res.status(isHealthy ? 200 : 503).json({
status: isHealthy ? 'OK' : 'Service Unavailable',
details: results
});
})
.catch(() => {
res.status(503).json({ status: 'Service Unavailable' });
});
};
}
const dbCheck = () => new Promise(resolve => {
// 检查数据库连接
resolve({ healthy: true, service: 'database' });
});
app.use(healthCheck({
checks: [dbCheck]
}));
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:中间件的版本兼容性问题
下一篇:中间件的最佳实践总结