为什么 Express

社区最大、生态最熟、文档最全——第一次写 Node Web 服务首选

npm install express

Express 5 在 2024 年稳定发布——支持原生 async/await,新项目用 5

最小服务

import express from 'express';

const app = express();

app.get('/', (req, res) => {
    res.send('Hello, Express');
});

app.listen(3000, () => {
    console.log('Server on http://localhost:3000');
});

路由

app.get('/users', (req, res) => res.send('list'));
app.get('/users/:id', (req, res) => res.send(`user ${req.params.id}`));
app.post('/users', (req, res) => res.status(201).send('created'));
app.put('/users/:id', ...);
app.delete('/users/:id', ...);
app.patch('/users/:id', ...);

// 一个路径多个方法
app.route('/users')
    .get((req, res) => ...)
    .post((req, res) => ...);

路由参数

app.get('/users/:id', (req, res) => {
    res.send(req.params.id);                     // /users/42 → '42'
});

app.get('/posts/:year/:month', (req, res) => {
    res.send(`${req.params.year}-${req.params.month}`);
});

// 通配
app.get('/files/*', (req, res) => {
    res.send(req.params[0]);                     // /files/a/b/c → 'a/b/c'
});

// 正则
app.get(/\/users\/(\d+)/, (req, res) => {
    res.send(req.params[0]);
});

查询参数

// /search?q=hello&page=2
app.get('/search', (req, res) => {
    res.send(`${req.query.q}, page ${req.query.page}`);
});

请求体

// 内置中间件
app.use(express.json());                          // 解析 JSON body
app.use(express.urlencoded({ extended: true })); // 解析 form

app.post('/users', (req, res) => {
    console.log(req.body);                        // 自动解析的 JSON
    res.status(201).json(req.body);
});

响应

res.send('text');                                // 文本
res.json({ ok: true });                          // JSON(自动 Content-Type)
res.status(404).send('Not Found');               // 状态码
res.redirect('/new-path');                        // 302
res.redirect(301, '/permanent');                  // 永久重定向
res.sendFile('/path/to/file.pdf');               // 发文件
res.download('/path/to/file.pdf');               // 触发下载
res.cookie('name', 'value');                     // 设 cookie

静态文件

app.use(express.static('public'));
// public/css/style.css → http://localhost:3000/css/style.css

app.use('/static', express.static('public'));    // 加前缀
// public/css/style.css → /static/css/style.css

路由组(Router)

大型应用拆分路由:

// routes/users.js
import { Router } from 'express';

const router = Router();

router.get('/', (req, res) => res.send('list'));
router.get('/:id', (req, res) => res.send(req.params.id));
router.post('/', (req, res) => res.send('created'));

export default router;

// app.js
import usersRouter from './routes/users.js';
app.use('/users', usersRouter);

模板引擎(如果做 SSR)

npm install ejs
app.set('view engine', 'ejs');
app.set('views', './views');

app.get('/', (req, res) => {
    res.render('home', { name: 'Alice' });    // 渲染 views/home.ejs
});

views/home.ejs:

<h1>Hello <%= name %></h1>

错误处理(特殊中间件)

// 4 个参数的中间件 = 错误处理器
app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).send('Something broke');
});

// 触发
app.get('/boom', (req, res, next) => {
    next(new Error('Boom!'));            // 跳到 error handler
});

// Express 5 自动捕获 async 错误
app.get('/async', async (req, res) => {
    const data = await someAsync();      // 抛错自动到 error handler
    res.json(data);
});

Express 4 需要手动 next(err)express-async-errors 包;Express 5 原生支持

部署到生产

import express from 'express';

const app = express();

// 信任反向代理(拿真实 IP)
app.set('trust proxy', 1);

// 通用中间件
app.use(express.json({ limit: '1mb' }));
app.use(express.urlencoded({ extended: true, limit: '1mb' }));

// 路由
app.use('/api', apiRouter);

// 错误处理(永远最后)
app.use((err, req, res, next) => {
    console.error(err);
    res.status(err.status || 500).json({
        error: err.message,
    });
});

app.listen(process.env.PORT || 3000);

  • Express 5 之前不会自动捕获 async 错误——升级到 5 或用 express-async-errors
  • app.use 顺序很重要——先注册先生效
  • 不设 trust proxyreq.ip 永远是反代 IP
  • 大 body 默认拒绝——express.json({ limit: '10mb' }) 调整

下一篇:Express 中间件深入。