Appearance
Express中间件
中间件函数是Express框架的核心概念,它能够访问请求对象(req)、响应对象(res)和next函数。中间件函数可以执行以下任务:
- 执行任何代码
- 修改请求和响应对象
- 结束请求-响应循环
- 调用栈中的下一个中间件函数
中间件类型
应用级中间件
应用级中间件绑定到应用实例使用app.use()或app.METHOD()函数。
javascript
const express = require('express');
const app = express();
// 普通中间件
app.use((req, res, next) => {
console.log('Time:', Date.now());
next();
});
// 路径特定中间件
app.use('/user/:id', (req, res, next) => {
console.log('Request Type:', req.method);
next();
});
// 路由中间件
app.get('/user/:id', (req, res, next) => {
console.log('Request URL:', req.originalUrl);
next();
}, (req, res) => {
res.send('User Info');
});
路由级中间件
路由级中间件与应用级中间件类似,但使用的是express.Router()实例。
javascript
const express = require('express');
const app = express();
const router = express.Router();
// 路由级中间件
router.use((req, res, next) => {
console.log('Time:', Date.now());
next();
});
router.get('/', (req, res) => {
res.send('Router homepage');
});
app.use('/router', router);
错误处理中间件
错误处理中间件有四个参数,必须有四个参数Express才会将其识别为错误处理中间件。
javascript
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
内置中间件
Express唯一的内置中间件是express.static(),用于提供静态文件。
javascript
app.use(express.static('public'));
app.use('/static', express.static('public')); // 自定义虚拟路径
第三方中间件
通过npm安装第三方中间件来扩展功能。
javascript
const cookieParser = require('cookie-parser');
const session = require('express-session');
app.use(cookieParser());
app.use(session());
中间件函数编写
基本中间件结构
javascript
const myMiddleware = (req, res, next) => {
// 执行中间件逻辑
console.log('Middleware executed');
// 调用next()继续执行下一个中间件
next();
};
app.use(myMiddleware);
条件中间件
javascript
const conditionalMiddleware = (req, res, next) => {
if (req.headers.authorization) {
next(); // 如果有授权头,继续
} else {
res.status(401).send('Unauthorized');
}
};
app.use('/api', conditionalMiddleware);
参数化中间件
javascript
const createMiddleware = (options) => {
return (req, res, next) => {
console.log(`Option value: ${options.value}`);
next();
};
};
app.use(createMiddleware({ value: 'example' }));
常用内置中间件
body解析中间件
javascript
// 解析JSON请求体
app.use(express.json());
// 解析URL编码的请求体
app.use(express.urlencoded({ extended: true }));
// 解析文本请求体
app.use(express.text());
// 解析原始请求体
app.use(express.raw());
配置选项
javascript
// JSON解析配置
app.use(express.json({
type: 'application/json',
limit: '10mb', // 限制请求体大小
strict: true // 严格模式,只解析数组和对象
}));
// URL编码配置
app.use(express.urlencoded({
extended: false, // 使用querystring库
limit: '10mb'
}));
第三方中间件
CORS中间件
javascript
const cors = require('cors');
// 启用所有CORS请求
app.use(cors());
// 配置CORS
app.use(cors({
origin: ['http://localhost:3000', 'https://example.com'],
credentials: true
}));
日志中间件
javascript
const morgan = require('morgan');
// 使用预定义格式
app.use(morgan('combined'));
// 自定义格式
app.use(morgan(':method :url :status :res[content-length] - :response-time ms'));
安全中间件
javascript
const helmet = require('helmet');
// 设置安全头
app.use(helmet());
// 配置特定安全头
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"]
}
}
}));
中间件执行顺序
中间件按照定义的顺序执行:
javascript
app.use((req, res, next) => {
console.log('Middleware 1');
next();
});
app.use((req, res, next) => {
console.log('Middleware 2');
next();
});
app.get('/', (req, res) => {
res.send('Hello World');
});
// 输出:
// Middleware 1
// Middleware 2
路径特定中间件
javascript
// 只在/api路径下执行
app.use('/api', (req, res, next) => {
console.log('API middleware');
next();
});
// 多个路径
app.use(['/api', '/admin'], (req, res, next) => {
console.log('API or Admin middleware');
next();
});
中间件最佳实践
1. 错误处理
javascript
// 正确的错误处理中间件
app.use((err, req, res, next) => {
console.error(err.stack);
if (process.env.NODE_ENV === 'production') {
res.status(500).json({ error: 'Internal Server Error' });
} else {
res.status(500).json({ error: err.message });
}
});
2. 异步错误处理
javascript
// 处理异步错误
const asyncHandler = (fn) => (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);
}));
3. 中间件复用
javascript
// 认证中间件
const authenticate = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
// 验证token
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (err) {
res.status(401).json({ error: 'Invalid token' });
}
};
// 在需要认证的路由中使用
app.get('/profile', authenticate, (req, res) => {
res.json(req.user);
});
4. 中间件配置
javascript
// 可配置的中间件
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100 // 限制每个IP 15分钟内最多100个请求
});
app.use(limiter);
中间件模式
中间件链
javascript
const middleware1 = (req, res, next) => {
req.timestamp = Date.now();
next();
};
const middleware2 = (req, res, next) => {
req.processed = true;
next();
};
app.get('/example', [middleware1, middleware2], (req, res) => {
res.json({
timestamp: req.timestamp,
processed: req.processed
});
});
条件中间件应用
javascript
// 仅在开发环境使用日志
if (process.env.NODE_ENV === 'development') {
app.use(morgan('dev'));
}
// 根据路径条件应用中间件
app.use((req, res, next) => {
if (req.path.startsWith('/api/')) {
// API特定中间件
req.isApiRequest = true;
}
next();
});
中间件性能考虑
1. 避免不必要的中间件
javascript
// 避免在所有路由上应用不必要的中间件
app.use('/api', expensiveMiddleware); // 只在需要的地方应用
2. 中间件缓存
javascript
// 使用缓存避免重复计算
const memoryCache = require('memory-cache');
const cachedMiddleware = (req, res, next) => {
const key = '__middleware__' + req.originalUrl || req.url;
const cachedBody = memoryCache.get(key);
if (cachedBody) {
res.send(cachedBody);
} else {
res.sendResponse = res.send;
res.send = (body) => {
memoryCache.put(key, body, 1000 * 10); // 缓存10秒
res.sendResponse(body);
};
next();
}
};
Express中间件是构建可扩展和可维护应用的关键。通过合理使用中间件,可以将应用逻辑分解为可重用的组件,提高代码的组织性和可维护性。