Skip to content

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.json

3. 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)。

中间件类型

  1. 应用级中间件
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');
});
  1. 路由级中间件
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);
  1. 错误处理中间件
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);
});
  1. 内置中间件
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());
  1. 第三方中间件
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 handler

5. 请求和响应对象

问题: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 应用的性能?

答案

优化策略

  1. 使用压缩
javascript
const compression = require('compression');
app.use(compression());
  1. 静态文件缓存
javascript
app.use(express.static('public', {
  maxAge: '1d',  // 缓存一天
  etag: true,
  lastModified: true
}));
  1. 启用 Keep-Alive
javascript
const server = app.listen(3000);
server.keepAliveTimeout = 65000;
server.headersTimeout = 66000;
  1. 使用集群模式
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);
}
  1. 数据库连接池
javascript
const pool = mysql.createPool({
  connectionLimit: 10,
  host: 'localhost',
  user: 'root',
  password: 'password',
  database: 'mydb'
});
  1. 使用 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());