The deployment and configuration management of middleware
The Concept and Role of Middleware
Express middleware is essentially a function that has access to the request object (req
), the response object (res
), and the next middleware function in the application's request-response cycle. These functions can perform the following operations:
- Modify the request and response objects
- End the request-response cycle
- Call the next middleware in the stack
// Simplest middleware example
app.use((req, res, next) => {
console.log('Time:', Date.now());
next();
});
Types of Middleware
Express middleware can be divided into the following main types:
- Application-Level Middleware
// Application-level middleware loaded using app.use()
app.use('/user/:id', (req, res, next) => {
console.log('Request Type:', req.method);
next();
});
- Router-Level Middleware
// Router-specific middleware
router.get('/user/:id', (req, res, next) => {
console.log('Request URL:', req.originalUrl);
next();
}, (req, res) => {
res.send('User Info');
});
- Error-Handling Middleware
// Error-handling middleware requires four parameters
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
- Built-In Middleware
// Express built-in static file middleware
app.use(express.static('public'));
- Third-Party Middleware
// Common third-party middleware example
const bodyParser = require('body-parser');
app.use(bodyParser.json());
Middleware Loading Order
The execution order of middleware is crucial, as Express executes middleware in the order they are loaded:
// First middleware
app.use((req, res, next) => {
console.log('First middleware');
next();
});
// Second middleware
app.use('/user/:id', (req, res, next) => {
console.log('Request Type:', req.method);
next();
});
// Route handler
app.get('/user/:id', (req, res) => {
res.send('User Info');
});
Middleware Configuration Management
Proper configuration management can improve the maintainability of middleware:
- Environment Variable Configuration
const morgan = require('morgan');
if (process.env.NODE_ENV === 'development') {
app.use(morgan('dev'));
}
- Configuration File Separation
// config/middlewares.js
module.exports = (app) => {
app.use(require('helmet')());
app.use(require('compression')());
};
- Conditional Middleware Loading
const setupMiddlewares = (app) => {
// Basic middleware
app.use(express.json());
// Development-only middleware
if (process.env.NODE_ENV !== 'production') {
app.use(require('morgan')('dev'));
app.use(require('errorhandler')());
}
};
Common Middleware Configuration Examples
- CORS Configuration
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 Security Middleware
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 minutes
max: 100, // Limit each IP to 100 requests
message: 'Too many requests from this IP, please try again later'
});
app.use('/api/', limiter);
Custom Middleware Development
Developing custom middleware can meet specific business needs:
- Request Logger Middleware
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'));
- Authentication Middleware
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' });
});
- Data Validation Middleware
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) => {
// Handle registration logic
});
Middleware Performance Optimization
- Middleware Caching
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) => {
// Fetch data from database
res.json({ data: 'Expensive data' });
});
- Parallel Middleware Processing
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
});
});
- Lazy Loading Middleware
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'));
Middleware Testing Strategies
- Unit Testing 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);
});
});
- Integration Testing Middleware Chain
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(() => {});
});
});
- Performance Testing Middleware
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();
});
});
});
Middleware Error Handling
- Asynchronous Error Handling
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);
}));
- Error Classification Handling
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);
});
- Error Logging Middleware
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);
Middleware Version Management
- API Version Control Middleware
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);
- Middleware Version Compatibility Check
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'));
Dynamic Middleware Loading
- On-Demand Middleware Loading
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));
- Feature Toggle Middleware
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');
});
Middleware Monitoring and Metrics
- Request Metrics Collection
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);
- Health Check Middleware
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 => {
// Check database connection
resolve({ healthy: true, service: 'database' });
});
app.use(healthCheck({
checks: [dbCheck]
}));
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:中间件的版本兼容性问题
下一篇:中间件的最佳实践总结