背景与核心机制
Node.js 是基于 Chrome V8 引擎的 JavaScript 运行时,常用于构建高性能、可扩展的网络应用。它的主要特点包括非阻塞 I/O、事件驱动、单线程但支持高并发、跨平台、npm 包生态丰富,适合 I/O 密集型场景。Express 则是 Node.js 上快速、极简的 Web 框架,核心能力包括路由系统、中间件、模板引擎集成、错误处理机制,以及对 HTTP 请求和响应的简化处理。
事件循环是 Node.js 实现非阻塞 I/O 的核心机制,它让 Node.js 在不使用多线程的情况下处理大量并发操作。事件循环主要包含定时器阶段(setTimeout、setInterval 回调)、待定回调阶段(系统操作回调,如 TCP 错误)、闲置/准备阶段(内部使用)、轮询阶段(检索新的 I/O 事件)、检查阶段(setImmediate 回调)和关闭回调阶段(如 socket.on('close'))。
回调地狱指多层嵌套回调导致代码难读难维护。常见规避方式包括使用 Promise 和 async/await、模块化拆分回调函数、使用 async.js 等流程控制库,以及遵循错误优先回调模式。
错误处理与中间件模型
Node.js 的错误处理方式主要有:回调函数中的错误优先模式、try/catch 处理同步代码错误、Promise 的 .catch()、process.on('uncaughtException') 全局捕获,以及已弃用的 domain 模块。对于异步代码,原文建议使用 async/await 结合 try/catch。
Express 中间件是在请求和响应周期中访问 req、res 和 next 的函数。它可以执行任意代码、修改请求和响应对象、结束请求-响应周期,或调用堆栈中的下一个中间件。中间件分为应用级中间件(app.use)、路由级中间件(router.use)、错误处理中间件、内置中间件(express.static)和第三方中间件(body-parser)。
路由定义采用 app.METHOD(PATH, HANDLER) 形式,路由参数通过 req.params 获取。路由也可以模块化:
- // 基本路由
- app.METHOD(PATH, HANDLER)
- // 示例
- app.get('/users', (req, res) => {
- res.send('Get all users');
- });
- // 路由参数
- app.get('/users/:id', (req, res) => {
- res.send(`Get user with ID ${req.params.id}`);
- });
- // 路由模块化
- const router = express.Router();
- router.get('/', (req, res) => {...});
- app.use('/users', router);
复制代码
安全方面,原文列出使用 helmet 设置安全 HTTP 头、防止 XSS 和 SQL 注入、使用 csurf 防止 CSRF、限制请求体大小(express.json({ limit: '10kb' }))、使用 rate-limiting 防止暴力攻击、正确处理用户输入输出、使用 HTTPS、保持依赖项更新等措施。
JWT、文件上传与跨域配置
JWT 是用于在各方之间以 JSON 对象安全传输信息的开放标准。Express 中通常安装 jsonwebtoken 和 express-jwt:用户登录时生成 token,客户端存储 token 并在请求头中发送,服务器验证 token。原文示例:
- const jwt = require('jsonwebtoken');
- const expressJwt = require('express-jwt');
- // 生成 token
- const token = jwt.sign({ userId: user.id }, 'secret', { expiresIn: '1h' });
- // 验证中间件
- app.use(expressJwt({ secret: 'secret', algorithms: ['HS256'] }));
- // 受保护路由
- app.get('/protected', (req, res) => {
- res.send(`Hello ${req.user.userId}`);
- });
复制代码
文件上传使用 multer 中间件,dest 指定上传目录,upload.single('file') 处理单文件,上传信息在 req.file 中:
- const multer = require('multer');
- const upload = multer({ dest: 'uploads/' });
- app.post('/upload', upload.single('file'), (req, res) => {
- // req.file 是上传的文件信息
- res.send('File uploaded successfully');
- });
复制代码
CORS 是跨源资源共享机制,允许网页从不同域的服务器请求资源。Express 中可使用 cors 中间件,既可全局启用,也可配置 origin、methods、allowedHeaders,或只在单个路由启用:
- const cors = require('cors');
- // 全部路由启用
- app.use(cors());
- // 或配置特定选项
- app.use(cors({
- origin: 'https://example.com',
- methods: ['GET', 'POST'],
- allowedHeaders: ['Content-Type']
- }));
- // 或单个路由启用
- app.get('/products', cors(), (req, res) => {
- res.json({msg: 'This is CORS-enabled for a Single Route'});
- });
复制代码
用户认证方面,原文区分基于会话的认证(express-session、内存或 Redis 存储、passport.js)、基于令牌的认证(JWT、passport-oauth)和第三方认证(Google、Facebook、Twitter 等 passport 策略)。Passport 本地策略示例:
- const passport = require('passport');
- const LocalStrategy = require('passport-local').Strategy;
- passport.use(new LocalStrategy(
- (username, password, done) => {
- User.findOne({ username }, (err, user) => {
- if (err) return done(err);
- if (!user) return done(null, false);
- if (!user.verifyPassword(password)) return done(null, false);
- return done(null, user);
- });
- }
- ));
- app.post('/login',
- passport.authenticate('local', {
- successRedirect: '/',
- failureRedirect: '/login'
- })
- );
复制代码
多核、性能与调试
cluster 模块允许创建共享同一服务器端口的子进程,以利用多核 CPU。主进程管理工作进程,工作进程处理请求;工作进程崩溃时主进程可以重启它,从而提高可靠性。
Express 性能优化可从这些方向入手:使用 gzip 压缩、避免同步代码、按需加载中间件、使用 Redis 缓存、使用 Nginx 反向代理和负载均衡、优化数据库查询、使用 PM2 等进程管理器、压缩静态资源、使用 CDN。
调试 Node.js 应用可使用 console.log、内置调试器 node inspect、Chrome DevTools(--inspect 标志)、VS Code 调试工具、ndb、debug 模块,以及 winston、morgan 等日志工具;性能分析可使用 --prof 标志。process 是全局对象,常用信息包括 process.env(环境变量)、process.argv(命令行参数)、process.cwd()(当前工作目录)、process.exit()(退出进程)、process.memoryUsage()(内存使用情况)、process.pid(进程 ID),以及 process.on() 监听 exit、uncaughtException 等事件。
流、Buffer、测试与 RESTful 约束
流用于处理读写文件、网络通信等操作,适合高效处理大量数据。类型包括 Readable(可读流,如 fs.createReadStream)、Writable(可写流,如 fs.createWriteStream)、Duplex(双工流,如 net.Socket)和 Transform(转换流,如 zlib.createGzip)。工作模式包括流动模式(数据自动读取)和暂停模式(必须显式调用 read())。
Buffer 类用于处理二进制数据,在 TCP 流、文件系统操作等需要处理原始二进制数据的场景中使用。主要方法包括 Buffer.alloc(size)、Buffer.from(array/string)、buf.toString()、buf.write() 和 Buffer.concat()。
Express 单元测试可使用 Jest、Mocha 等测试框架,结合 Chai 断言库和 supertest 测试 HTTP 请求,并模拟数据库及其他依赖,覆盖路由、中间件和业务逻辑。示例:
- const request = require('supertest');
- const app = require('../app');
- describe('GET /users', () => {
- it('responds with JSON', async () => {
- const response = await request(app)
- .get('/users')
- .expect('Content-Type', /json/)
- .expect(200);
- expect(response.body).toBeInstanceOf(Array);
- });
- });
复制代码
RESTful API 设计原则包括客户端-服务器分离、无状态、可缓存、统一接口、分层系统、按需代码(可选)、资源标识(URI)、通过表示操作资源、自描述消息,以及超媒体作为应用状态引擎(HATEOAS)。这些约束与上面的中间件、认证、错误处理和性能优化实践结合,既能用于面试复习,也能落到实际 Express 项目的代码排查与设计中。 |