Middleware mechanism and execution flow
Middleware Mechanism and Execution Flow
Express's middleware mechanism is one of its core features, allowing developers to insert custom processing logic into the request and response cycle. This mechanism is organized in the form of a function stack, where each middleware function can access the request object, response object, and a reference to the next middleware function.
Basic Structure of Middleware
A typical Express middleware function consists of three parameters: req
, res
, and next
. When the middleware completes its task, it can call next()
to pass control to the next middleware or directly end the request-response cycle.
const express = require('express');
const app = express();
// Simplest logging middleware
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
});
// Route handling middleware
app.get('/', (req, res) => {
res.send('Hello World');
});
app.listen(3000);
Types of Middleware
Express supports various types of middleware, each differing in invocation timing and scope:
- Application-level middleware: Bound to the application instance via
app.use()
orapp.METHOD()
- Router-level middleware: Bound to a router instance via
router.use()
- Error-handling middleware: Accepts four parameters
(err, req, res, next)
- Built-in middleware: Such as
express.static
- Third-party middleware: Such as
body-parser
// Application-level middleware example
app.use(express.json()); // Parse JSON request body
app.use(express.urlencoded({ extended: true })); // Parse URL-encoded request body
// Error-handling middleware example
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
Middleware Execution Order
The execution order of middleware is crucial. Express executes them sequentially in the order they are registered, determining how requests are processed and responses are constructed.
app.use((req, res, next) => {
console.log('First middleware');
next();
});
app.use((req, res, next) => {
console.log('Second middleware');
next();
});
app.get('/user', (req, res) => {
res.send('User profile');
});
In the example above, when accessing the /user
path, the console will output "First middleware" and "Second middleware" in sequence.
Middleware Flow Control
The next()
function is key to controlling middleware flow. It can:
- Pass control to the next middleware
- Skip remaining middleware
- Pass errors to error-handling middleware
// Token validation middleware
app.use('/api', (req, res, next) => {
const token = req.headers['authorization'];
if (!token) {
return res.status(401).send('Unauthorized');
}
next();
});
// This middleware executes only if token validation passes
app.get('/api/data', (req, res) => {
res.json({ data: 'secret' });
});
Asynchronous Middleware Handling
Modern Express supports asynchronous middleware functions using async/await
syntax:
app.use(async (req, res, next) => {
try {
const user = await User.findById(req.userId);
req.user = user;
next();
} catch (err) {
next(err);
}
});
Middleware Composition and Reuse
Express provides Router
instances to organize and reuse middleware:
const router = express.Router();
// Add middleware to all routes
router.use((req, res, next) => {
console.log('Time:', Date.now());
next();
});
// Define routes
router.get('/user/:id', (req, res) => {
res.send(`User ID: ${req.params.id}`);
});
// Mount the router to the app
app.use('/api', router);
Error Handling in Middleware
Error-handling middleware requires four parameters and is typically placed after other middleware:
app.get('/error', (req, res, next) => {
const err = new Error('Something went wrong');
next(err); // Pass to error-handling middleware
});
// Error-handling middleware
app.use((err, req, res, next) => {
res.status(500).json({
error: err.message,
stack: process.env.NODE_ENV === 'development' ? err.stack : undefined
});
});
Performance Considerations for Middleware
Middleware is powerful but can impact performance if misused:
- Avoid unnecessary computations in middleware
- Terminate invalid requests early
- Use caching judiciously
// Performance optimization example: Caching static resources
const cacheMiddleware = (req, res, next) => {
res.set('Cache-Control', 'public, max-age=3600');
next();
};
app.use('/static', cacheMiddleware, express.static('public'));
Practical Use Cases for Middleware
- Request logging: Record detailed information for each request
- Authentication: Validate user credentials
- Data validation: Check request data validity
- Response formatting: Standardize API response formats
- Rate limiting: Prevent API abuse
// Request logging middleware
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`${req.method} ${req.originalUrl} ${res.statusCode} ${duration}ms`);
});
next();
});
// Rate-limiting middleware
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // Limit 100 requests per IP
});
app.use('/api/', limiter);
Testing and Debugging Middleware
Testing middleware requires simulating request and response objects:
// Testing authentication middleware
test('auth middleware rejects requests without token', () => {
const req = {
headers: {}
};
const res = {
status: jest.fn().mockReturnThis(),
send: jest.fn()
};
const next = jest.fn();
authMiddleware(req, res, next);
expect(res.status).toHaveBeenCalledWith(401);
expect(res.send).toHaveBeenCalledWith('Unauthorized');
expect(next).not.toHaveBeenCalled();
});
Advanced Middleware Patterns
- Middleware factories: Create configurable middleware
- Middleware composition: Combine multiple middleware into one
- Conditional middleware: Apply middleware based on conditions
// Middleware factory example: Create a configurable logging middleware
function createLoggerMiddleware(format) {
return (req, res, next) => {
const message = format
.replace(':method', req.method)
.replace(':url', req.url);
console.log(message);
next();
};
}
app.use(createLoggerMiddleware(':method :url'));
// Conditional middleware example
function conditionalMiddleware(condition, middleware) {
return (req, res, next) => {
if (condition(req)) {
middleware(req, res, next);
} else {
next();
}
};
}
app.use(conditionalMiddleware(
req => req.path.startsWith('/api'),
bodyParser.json()
));
Middleware and Express 5 New Features
Express 5 introduces several improvements to middleware handling:
- Better asynchronous error handling
- Improved route parameter processing
- More flexible response methods
// Improved error handling in Express 5
app.get('/user/:id', async (req, res, next) => {
try {
const user = await User.find(req.params.id);
if (!user) throw new Error('User not found');
res.json(user);
} catch (err) {
next(err); // Automatically passed to error-handling middleware
}
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:路由系统与URL处理
下一篇:请求与响应对象详解