Appearance
Express 框架基础面试题
1. 什么是 Express?
问题:什么是 Express?它有什么特点?
答案: Express 是一个基于 Node.js 的极简、灵活的 Web 应用框架,它提供了一系列强大的特性来开发 Web 和移动应用。
主要特点:
- 极简主义:核心功能精简,通过中间件扩展
- 灵活性高:不强制项目结构,自由度高
- 路由强大:支持参数化路由、路由处理器
- 中间件丰富:庞大的中间件生态系统
- 性能优秀:轻量级,性能开销小
- 社区活跃:文档完善,社区支持好
核心概念:
- 路由(Routing):定义应用的端点及响应方式
- 中间件(Middleware):处理请求和响应的函数
- 请求/响应对象:封装 HTTP 请求和响应
2. Express 的基本使用
问题:如何创建一个基本的 Express 应用?
答案:
基本示例:
javascript
const express = require('express');
const app = express();
const port = 3000;
// 中间件
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// 路由
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.get('/users/:id', (req, res) => {
const userId = req.params.id;
res.json({ id: userId, name: 'John' });
});
// POST 请求
app.post('/users', (req, res) => {
const userData = req.body;
res.status(201).json({ message: 'User created', data: userData });
});
// 启动服务器
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});应用结构:
my-express-app/
├── app.js # 应用入口
├── routes/ # 路由目录
│ ├── index.js
│ └── users.js
├── controllers/ # 控制器
│ └── userController.js
├── middleware/ # 自定义中间件
│ └── auth.js
├── models/ # 数据模型
│ └── user.js
└── package.json3. Express 的路由系统
问题:Express 的路由系统是如何工作的?
答案:
路由定义:
javascript
const express = require('express');
const router = express.Router();
// 基本路由
router.get('/', (req, res) => {
res.send('GET request');
});
router.post('/', (req, res) => {
res.send('POST request');
});
router.put('/:id', (req, res) => {
res.send(`PUT request for user ${req.params.id}`);
});
router.delete('/:id', (req, res) => {
res.send(`DELETE request for user ${req.params.id}`);
});
// 路由参数
router.get('/users/:userId/books/:bookId', (req, res) => {
res.send(req.params); // { userId: '...', bookId: '...' }
});
// 查询参数
router.get('/search', (req, res) => {
const { q, page, limit } = req.query;
res.send(`Search: ${q}, Page: ${page}`);
});
// 正则表达式路由
router.get(/.*fly$/, (req, res) => {
res.send('/.*fly$/'); // 匹配以 fly 结尾的路径
});
// 多个回调函数
router.get('/example',
(req, res, next) => {
console.log('First handler');
next();
},
(req, res) => {
res.send('Second handler');
}
);
// 使用数组
const cb0 = (req, res, next) => { next(); };
const cb1 = (req, res, next) => { next(); };
const cb2 = (req, res) => { res.send('Done'); };
router.get('/array', [cb0, cb1, cb2]);
module.exports = router;路由模块化:
javascript
// routes/users.js
const express = require('express');
const router = express.Router();
router.get('/', getAllUsers);
router.get('/:id', getUserById);
router.post('/', createUser);
router.put('/:id', updateUser);
router.delete('/:id', deleteUser);
module.exports = router;
// app.js
const userRoutes = require('./routes/users');
app.use('/users', userRoutes);4. Express 中间件
问题:什么是 Express 中间件?有哪些类型?
答案:
中间件概念: 中间件是函数,可以访问请求对象(req)、响应对象(res)和应用程序的请求-响应循环中的下一个中间件函数(next)。
中间件类型:
- 应用级中间件:
javascript
// 全局中间件
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();
});
// 特定 HTTP 方法
app.get('/user/:id', (req, res, next) => {
res.send('USER');
});- 路由级中间件:
javascript
const router = express.Router();
// 路由级中间件
router.use((req, res, next) => {
console.log('Router middleware');
next();
});
router.get('/', (req, res) => {
res.send('Router home');
});
app.use('/router', router);- 错误处理中间件:
javascript
// 错误处理中间件(4个参数)
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
error: 'Something went wrong!',
message: err.message
});
});
// 自定义错误类
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
this.statusCode = 400;
}
}
app.use((err, req, res, next) => {
if (err instanceof ValidationError) {
return res.status(400).json({ error: err.message });
}
next(err);
});- 内置中间件:
javascript
// 解析 JSON 请求体
app.use(express.json());
// 解析 URL 编码请求体
app.use(express.urlencoded({ extended: true }));
// 静态文件服务
app.use(express.static('public'));
app.use('/static', express.static('public'));
// 解析 Cookie
app.use(express.cookieParser());- 第三方中间件:
javascript
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const compression = require('compression');
// CORS
app.use(cors({
origin: 'http://example.com',
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
// 安全头
app.use(helmet());
// 日志
app.use(morgan('combined'));
// 压缩
app.use(compression());中间件执行顺序:
javascript
// 按添加顺序执行
app.use((req, res, next) => {
console.log('First');
next();
});
app.use((req, res, next) => {
console.log('Second');
next();
});
app.get('/', (req, res) => {
console.log('Route handler');
res.send('Done');
});
// 输出:First -> Second -> Route handler5. 请求和响应对象
问题:Express 的 req 和 res 对象有哪些常用属性和方法?
答案:
请求对象(req):
javascript
// 常用属性
req.params // URL 参数 { id: '123' }
req.query // 查询参数 { search: 'keyword' }
req.body // 请求体(需要 body-parser)
req.headers // 请求头
req.cookies // Cookie(需要 cookie-parser)
req.path // 请求路径
req.method // HTTP 方法
req.url // 完整 URL
req.ip // 客户端 IP
req.protocol // 协议(http/https)
// 常用方法
req.get('Content-Type') // 获取请求头
req.is('application/json') // 检查 Content-Type
req.accepts('json') // 检查 Accept 头响应对象(res):
javascript
// 发送响应
res.send('Hello'); // 发送字符串
res.json({ name: 'John' }); // 发送 JSON
res.sendFile('/path/to/file'); // 发送文件
res.render('template', data); // 渲染模板
// 状态码
res.status(404).send('Not Found');
res.status(500).json({ error: 'Server Error' });
// 重定向
res.redirect('/new-path');
res.redirect(301, '/permanent-path');
// 设置头
res.set('Content-Type', 'text/plain');
res.set({
'Content-Type': 'text/plain',
'X-Custom-Header': 'value'
});
// Cookie
res.cookie('name', 'value', {
maxAge: 900000,
httpOnly: true,
secure: true
});
res.clearCookie('name');
// 下载
res.download('/path/to/file.pdf');
res.download('/path/to/file.pdf', 'report.pdf');
// 结束响应
res.end();
res.end('Data');6. Express 错误处理
问题:如何在 Express 中处理错误?
答案:
错误处理方式:
javascript
// 1. 同步错误自动捕获
app.get('/', (req, res) => {
throw new Error('Sync error'); // Express 会自动捕获
});
// 2. 异步错误需要 next()
app.get('/async', async (req, res, next) => {
try {
const data = await fetchData();
res.json(data);
} catch (error) {
next(error); // 传递给错误处理中间件
}
});
// 3. 集中式错误处理
app.use((err, req, res, next) => {
// 记录错误
console.error('Error:', err);
// 根据环境返回不同信息
if (process.env.NODE_ENV === 'development') {
res.status(err.status || 500).json({
error: err.message,
stack: err.stack
});
} else {
res.status(err.status || 500).json({
error: 'Internal Server Error'
});
}
});
// 4. 404 处理
app.use((req, res, next) => {
res.status(404).json({ error: 'Not Found' });
});异步错误处理包装器:
javascript
// 包装异步路由处理器
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
// 使用
app.get('/users', asyncHandler(async (req, res) => {
const users = await User.findAll();
res.json(users);
}));7. Express 应用配置
问题:如何配置 Express 应用?
答案:
常用配置:
javascript
const express = require('express');
const app = express();
// 设置环境
app.set('env', 'development');
// 信任代理(用于获取真实 IP)
app.set('trust proxy', true);
// 设置视图引擎
app.set('view engine', 'ejs');
app.set('views', './views');
// 禁用 X-Powered-By 头
app.disable('x-powered-by');
// 启用严格路由
app.enable('strict routing');
// 大小写敏感路由
app.enable('case sensitive routing');
// 获取配置
console.log(app.get('env')); // 获取环境
console.log(app.enabled('trust proxy')); // 检查是否启用
// 环境特定配置
if (app.get('env') === 'development') {
app.use(morgan('dev'));
app.use(errorHandler());
}8. Express 性能优化
问题:如何优化 Express 应用的性能?
答案:
优化策略:
- 使用压缩:
javascript
const compression = require('compression');
app.use(compression());- 静态文件缓存:
javascript
app.use(express.static('public', {
maxAge: '1d', // 缓存一天
etag: true,
lastModified: true
}));- 启用 Keep-Alive:
javascript
const server = app.listen(3000);
server.keepAliveTimeout = 65000;
server.headersTimeout = 66000;- 使用集群模式:
javascript
const cluster = require('cluster');
const os = require('os');
if (cluster.isMaster) {
const numCPUs = os.cpus().length;
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
} else {
app.listen(3000);
}- 数据库连接池:
javascript
const pool = mysql.createPool({
connectionLimit: 10,
host: 'localhost',
user: 'root',
password: 'password',
database: 'mydb'
});- 使用 Redis 缓存:
javascript
const redis = require('redis');
const client = redis.createClient();
app.get('/data', async (req, res) => {
const cached = await client.get('data');
if (cached) {
return res.json(JSON.parse(cached));
}
const data = await fetchData();
await client.setex('data', 3600, JSON.stringify(data));
res.json(data);
});9. Express 安全最佳实践
问题:使用 Express 时有哪些安全最佳实践?
答案:
安全措施:
javascript
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const cors = require('cors');
const mongoSanitize = require('express-mongo-sanitize');
const xss = require('xss-clean');
const hpp = require('hpp');
// 1. 安全头
app.use(helmet());
// 2. 限流
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 分钟
max: 100 // 每个 IP 100 个请求
});
app.use('/api/', limiter);
// 3. CORS
app.use(cors({
origin: process.env.ALLOWED_ORIGINS?.split(',') || 'http://localhost:3000',
credentials: true
}));
// 4. 数据清理
app.use(mongoSanitize()); // 防止 MongoDB 注入
app.use(xss()); // 防止 XSS 攻击
app.use(hpp()); // 防止 HTTP 参数污染
// 5. 输入验证
const { body, validationResult } = require('express-validator');
app.post('/user',
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 8 }),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// 处理请求
}
);
// 6. Cookie 安全
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === 'production',
httpOnly: true,
maxAge: 3600000
}
}));10. Express 与中间件生态系统
问题:Express 有哪些常用的中间件?
答案:
常用中间件:
javascript
// 日志
const morgan = require('morgan');
app.use(morgan('combined'));
// 解析请求体
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// Cookie 解析
const cookieParser = require('cookie-parser');
app.use(cookieParser());
// Session
const session = require('express-session');
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true
}));
// 文件上传
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });
app.post('/upload', upload.single('file'), (req, res) => {
res.send('File uploaded');
});
// 认证
const passport = require('passport');
app.use(passport.initialize());
app.use(passport.session());
// 请求验证
const { body, validationResult } = require('express-validator');
// 跨域
const cors = require('cors');
app.use(cors());
// 压缩
const compression = require('compression');
app.use(compression());
// 安全
const helmet = require('helmet');
app.use(helmet());