Skip to content

Express 框架 ​

Express 是 Node.js 最流行的 Web 框架,基于 http 模块封装,提供路由、中间件等强大功能。

安装与脚手架 ​

手动创建 ​

bash
npm init -y
npm install express
npm init -y
npm install express

使用生成器 ​

bash
npx express-generator
# 或全局安装
npm install -g express-generator
express myapp
cd myapp && npm install
npx express-generator
# 或全局安装
npm install -g express-generator
express myapp
cd myapp && npm install

生成器创建的目录结构:

myapp/
├── bin/         # HTTP 服务启动文件
│   └── www
├── public/      # 静态文件
├── routes/      # 路由模块
│   ├── index.js
│   └── users.js
├── views/       # 视图模板
├── app.js       # 主程序
└── package.json
myapp/
├── bin/         # HTTP 服务启动文件
│   └── www
├── public/      # 静态文件
├── routes/      # 路由模块
│   ├── index.js
│   └── users.js
├── views/       # 视图模板
├── app.js       # 主程序
└── package.json

基本用法 ​

js
const express = require('express')
const app = express()
const port = 3000

// 路由
app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.listen(port, () => {
  console.log(`Server running at http://localhost:${port}`)
})
const express = require('express')
const app = express()
const port = 3000

// 路由
app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.listen(port, () => {
  console.log(`Server running at http://localhost:${port}`)
})

静态服务 ​

使用 express.static() 中间件提供静态文件服务:

js
const path = require('path')

app.use(express.static(path.join(__dirname, 'public')))
// public 目录下的文件可直接通过 URL 访问
const path = require('path')

app.use(express.static(path.join(__dirname, 'public')))
// public 目录下的文件可直接通过 URL 访问

API 服务 ​

请求方法 ​

js
app.get('/api/users', (req, res) => {
  res.json({ data: [{ name: '张三' }] })
})

app.post('/api/users', (req, res) => {
  res.json({ success: true })
})

app.put('/api/users/:id', (req, res) => {
  res.json({ success: true })
})

app.delete('/api/users/:id', (req, res) => {
  res.json({ success: true })
})
app.get('/api/users', (req, res) => {
  res.json({ data: [{ name: '张三' }] })
})

app.post('/api/users', (req, res) => {
  res.json({ success: true })
})

app.put('/api/users/:id', (req, res) => {
  res.json({ success: true })
})

app.delete('/api/users/:id', (req, res) => {
  res.json({ success: true })
})

接收参数 ​

js
// 查询字符串参数 — GET /api/users?page=1
req.query.page    // '1'

// 路径参数 — GET /api/users/123
req.params.id     // '123'

// POST 请求体(需配置中间件)
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
req.body          // 请求体数据
// 查询字符串参数 — GET /api/users?page=1
req.query.page    // '1'

// 路径参数 — GET /api/users/123
req.params.id     // '123'

// POST 请求体(需配置中间件)
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
req.body          // 请求体数据

响应方法 ​

js
res.send('文本')              // 发送文本
res.json({ key: 'value' })    // 发送 JSON
res.sendFile(path)            // 发送文件
res.status(404).send('Not Found')  // 设置状态码
res.send('文本')              // 发送文本
res.json({ key: 'value' })    // 发送 JSON
res.sendFile(path)            // 发送文件
res.status(404).send('Not Found')  // 设置状态码

路由模块化 ​

使用 express.Router() 实现路由拆分:

js
// routes/users.js
const express = require('express')
const router = express.Router()

router.get('/list', (req, res) => {
  res.json([{ name: '张三' }])
})

router.post('/create', (req, res) => {
  res.json({ success: true })
})

module.exports = router
// routes/users.js
const express = require('express')
const router = express.Router()

router.get('/list', (req, res) => {
  res.json([{ name: '张三' }])
})

router.post('/create', (req, res) => {
  res.json({ success: true })
})

module.exports = router
js
// app.js
const userRoutes = require('./routes/users')
app.use('/api/users', userRoutes)
// 访问:GET /api/users/list
// app.js
const userRoutes = require('./routes/users')
app.use('/api/users', userRoutes)
// 访问:GET /api/users/list

中间件 ​

中间件是 Express 的核心机制,按顺序依次处理请求。

自定义中间件 ​

js
// 全局中间件(所有请求都会经过)
app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`)
  next()  // 交出控制权,继续下一个中间件
})

// 路由级中间件
app.use('/api', (req, res, next) => {
  console.log('API 请求')
  next()
})

// 错误处理中间件(4个参数)
app.use((err, req, res, next) => {
  console.error('错误:', err.message)
  res.status(500).send('服务器错误')
})
// 全局中间件(所有请求都会经过)
app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`)
  next()  // 交出控制权,继续下一个中间件
})

// 路由级中间件
app.use('/api', (req, res, next) => {
  console.log('API 请求')
  next()
})

// 错误处理中间件(4个参数)
app.use((err, req, res, next) => {
  console.error('错误:', err.message)
  res.status(500).send('服务器错误')
})

内置中间件 ​

js
app.use(express.json())              // 解析 JSON 请求体
app.use(express.urlencoded({ extended: true }))  // 解析 URL-encoded
app.use(express.static('public'))     // 静态文件服务
app.use(express.json())              // 解析 JSON 请求体
app.use(express.urlencoded({ extended: true }))  // 解析 URL-encoded
app.use(express.static('public'))     // 静态文件服务

跨域处理 (CORS) ​

手动设置中间件 ​

js
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*')
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')

  // 预检请求直接结束
  if (req.method === 'OPTIONS') {
    return res.sendStatus(204)
  }
  next()
})
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*')
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')

  // 预检请求直接结束
  if (req.method === 'OPTIONS') {
    return res.sendStatus(204)
  }
  next()
})

使用 cors 包 ​

bash
npm install cors
npm install cors
js
const cors = require('cors')

app.use(cors())  // 允许所有来源

// 配置选项
app.use(cors({
  origin: ['http://localhost:3000'],
  credentials: true,
  methods: 'GET, POST, PUT, DELETE, OPTIONS'
}))
const cors = require('cors')

app.use(cors())  // 允许所有来源

// 配置选项
app.use(cors({
  origin: ['http://localhost:3000'],
  credentials: true,
  methods: 'GET, POST, PUT, DELETE, OPTIONS'
}))

URLSearchParams ​

用于处理 URL 查询字符串:

js
const query = new URLSearchParams('foo=bar&baz=qux')

query.get('foo')          // 'bar'
query.has('foo')          // true
query.append('key', 'value')
query.set('foo', 'new')   // 修改
query.delete('foo')       // 删除
query.toString()          // 'baz=qux&key=value'

// 转对象
Object.fromEntries(new URLSearchParams('foo=bar'))
// { foo: 'bar' }
const query = new URLSearchParams('foo=bar&baz=qux')

query.get('foo')          // 'bar'
query.has('foo')          // true
query.append('key', 'value')
query.set('foo', 'new')   // 修改
query.delete('foo')       // 删除
query.toString()          // 'baz=qux&key=value'

// 转对象
Object.fromEntries(new URLSearchParams('foo=bar'))
// { foo: 'bar' }

常用工具包 ​

js
// cors — 跨域
const cors = require('cors')

// body-parser — 请求体解析(Express 4.16+ 已内置)
// 直接使用 express.json() / express.urlencoded()

// morgan — 请求日志
const morgan = require('morgan')
app.use(morgan('dev'))

// multer — 文件上传
const multer = require('multer')
const upload = multer({ dest: 'uploads/' })
app.post('/upload', upload.single('file'), (req, res) => {
  console.log(req.file)
})

// formidable — 文件上传(另一种方式)
const formidable = require('formidable')
app.post('/upload', (req, res) => {
  const form = new formidable.IncomingForm()
  form.parse(req, (err, fields, files) => {
    // fields: 普通字段, files: 上传文件
  })
})

// express-session — Session 中间件
const session = require('express-session')
app.use(session({
  secret: 'your-secret-key',
  resave: true,
  saveUninitialized: true
}))

// connect-mongo — Session 存储到 MongoDB
const MongoStore = require('connect-mongo')(session)
app.use(session({
  secret: 'key',
  store: new MongoStore({ url: 'mongodb://localhost:27017/myapp' }),
  resave: true,
  saveUninitialized: true
}))

// cookie-parser — Cookie 解析
const cookieParser = require('cookie-parser')
app.use(cookieParser())
// cors — 跨域
const cors = require('cors')

// body-parser — 请求体解析(Express 4.16+ 已内置)
// 直接使用 express.json() / express.urlencoded()

// morgan — 请求日志
const morgan = require('morgan')
app.use(morgan('dev'))

// multer — 文件上传
const multer = require('multer')
const upload = multer({ dest: 'uploads/' })
app.post('/upload', upload.single('file'), (req, res) => {
  console.log(req.file)
})

// formidable — 文件上传(另一种方式)
const formidable = require('formidable')
app.post('/upload', (req, res) => {
  const form = new formidable.IncomingForm()
  form.parse(req, (err, fields, files) => {
    // fields: 普通字段, files: 上传文件
  })
})

// express-session — Session 中间件
const session = require('express-session')
app.use(session({
  secret: 'your-secret-key',
  resave: true,
  saveUninitialized: true
}))

// connect-mongo — Session 存储到 MongoDB
const MongoStore = require('connect-mongo')(session)
app.use(session({
  secret: 'key',
  store: new MongoStore({ url: 'mongodb://localhost:27017/myapp' }),
  resave: true,
  saveUninitialized: true
}))

// cookie-parser — Cookie 解析
const cookieParser = require('cookie-parser')
app.use(cookieParser())

模板引擎 (EJS) ​

配置 ​

js
app.set('view engine', 'ejs')
app.set('views', __dirname + '/views')
// 使用 .html 后缀
app.engine('html', require('ejs').__express)
app.set('view engine', 'ejs')
app.set('views', __dirname + '/views')
// 使用 .html 后缀
app.engine('html', require('ejs').__express)

渲染 ​

js
app.get('/', (req, res) => {
  res.render('index', { title: '首页', message: 'Hello' })
})
app.get('/', (req, res) => {
  res.render('index', { title: '首页', message: 'Hello' })
})

EJS 语法 ​

ejs
<%= 变量 %>           <!-- 输出转义后的内容 -->
<%- 变量 %>           <!-- 输出原始 HTML -->
<% 代码 %>            <!-- 执行 JavaScript -->
<%# 注释 %>           <!-- EJS 注释 -->
<%- include('header') %>  <!-- 引入模板 -->

<!-- 循环 -->
<% for(let i = 0; i < arr.length; i++) { %>
  <li><%= arr[i] %></li>
<% } %>

<!-- 条件 -->
<% if (condition) { %>
  <p>条件成立</p>
<% } %>
<%= 变量 %>           <!-- 输出转义后的内容 -->
<%- 变量 %>           <!-- 输出原始 HTML -->
<% 代码 %>            <!-- 执行 JavaScript -->
<%# 注释 %>           <!-- EJS 注释 -->
<%- include('header') %>  <!-- 引入模板 -->

<!-- 循环 -->
<% for(let i = 0; i < arr.length; i++) { %>
  <li><%= arr[i] %></li>
<% } %>

<!-- 条件 -->
<% if (condition) { %>
  <p>条件成立</p>
<% } %>