环企首页
Express
概述
Express 是 Node.js 生态中最成熟、使用最广泛的 Web 框架。核心设计思想是"中间件",通过一个个函数对请求进行层层处理,形成洋葱模型。以"小而美"著称,核心只提供路由和中间件系统,其他功能(模板引擎、数据库、Session 等)均由社区生态补充。
主要特点:
- 中间件机制灵活,生态极为庞大
- 学习曲线平缓,文档完善
- 几乎所有 Node.js 项目的入门框架
- 适合快速原型、中小型 API 服务、微服务
典型使用场景:
- RESTful API 服务
- SSR 网站后端
- 微服务网关
- 前后端分离的 BFF 层
常用函数详解
应用实例
const express = require('express')
const app = express()
express.json([options])
内置中间件,解析 Content-Type: application/json 的请求体。
// 基础用法
app.use(express.json())
// 请求体存放在 req.body
app.post('/users', (req, res) => {
console.log(req.body) // { name: '张三', age: 25 }
res.json({ success: true })
})
// options 常用参数
app.use(express.json({
limit: '10mb', // 限制请求体大小,默认 100kb
strict: true, // 只解析严格 JSON(数组除外),默认 true
inflate: true, // 是否解压 gzip 内容,默认 true
type: 'application/json' // 识别哪种 Content-Type,默认 application/json
}))
express.urlencoded([options])
解析 application/x-www-form-urlencoded 格式的请求体(表单提交)。
app.use(express.urlencoded({ extended: true }))
// extended: true 允许嵌套对象(用 qs 库),false 用内置querystring
// 请求体同样在 req.body 中
app.post('/login', (req, res) => {
console.log(req.body) // { username: 'admin', password: '123456' }
})
express.static(root, [options])
托管静态文件目录(CSS/JS/图片等),直接通过 URL 访问。
// 将 public 目录托管为静态资源
app.use(express.static('public'))
// 虚拟路径前缀(访问 http://localhost:3000/assets/style.css)
app.use('/assets', express.static('public'))
// 常用 options
app.use(express.static('public', {
dotfiles: 'ignore', // 是否响应 . 开头的文件(ignore/allow/deny)
etag: true, // 生成 ETag,默认 true
extensions: ['html', 'htm'], // 访问时省略扩展名则尝试这些
index: 'index.html', // 默认首页文件名,false 禁用
maxAge: '1d', // Cache-Control max-age(秒或 ms 字符串)
redirect: true, // 路径末尾是否加 /
lastModified: true, // Last-Modified 头,默认 true
}))
express.Router([options])
创建路由分组实例,用于模块化路由。
// users.js
const router = express.Router()
router.get('/', (req, res) => { /* 列表 */ })
router.get('/:id', (req, res) => { /* 详情 */ })
router.post('/', (req, res) => { /* 创建 */ })
router.put('/:id', (req, res) => { /* 更新 */ })
router.delete('/:id', (req, res) => { /* 删除 */ })
module.exports = router
// app.js 中挂载
app.use('/users', require('./routes/users'))
// 最终路由: GET /users, GET /users/:id, POST /users ...
HTTP 方法
app.METHOD(path, ...handlers)
处理对应 HTTP 方法的路由,支持 get、post、put、patch、delete、head、options。
app.get('/users', (req, res) => { res.json([]) })
app.post('/users', (req, res) => { res.status(201).json({}) })
app.put('/users/:id', (req, res) => { res.json({}) })
app.patch('/users/:id', (req, res) => { res.json({}) })
app.delete('/users/:id', (req, res) => { res.status(204).send() })
// 任意方法(GET/POST/PUT/DELETE 都走这个)
app.all('/api/*', (req, res, next) => {
// 鉴权、日志等通用逻辑
next()
})
请求对象(Request)
通过 req 对象获取客户端传来的数据。
app.get('/users/:id/articles', (req, res) => {
// 路径参数
req.params.id // '123'
// 查询参数(URL ?后的键值对)
req.query.page // '1'
req.query.sort // 'name'
req.query.filter // { name: 'foo' } 当 key 重复时
// 请求头
req.headers['authorization']
req.get('Content-Type')
// 请求体(需配合 express.json())
req.body
// Cookie(需配合 cookie-parser)
req.cookies
// 当前请求路径(不含查询参数)
req.path // '/users/123/articles'
// 完整 URL(含协议、域名、端口)
req.originalUrl // '/users/123/articles?page=1'
// HTTP 版本
req.httpVersion // '1.1'
// 客户端 IP(需配合 trust proxy)
req.ip // '::1' 或 '192.168.1.1'
// 请求方法
req.method // 'GET'
// Content-Type(不含参数)
req.get('content-type') // 'application/json'
// 是否 AJAX 请求(通过 X-Requested-With 头判断)
req.xhr // true / false
// 路由
req.route // Route 对象
// 签名 cookie(需 cookie-parser signedCookies)
req.signedCookies
})
响应对象(Response)
通过 res 对象向客户端返回数据。
// 发送数据
res.send('hello') // 发送字符串,自动设 Content-Type: text/html
res.send(Buffer.from('data')) // 发送二进制
res.send({ success: true }) // 发送对象,自动 res.json()
res.send([1, 2, 3]) // 发送数组,自动 res.json()
// JSON 响应
res.json({ code: 0, data: [] })
res.jsonp({ code: 0, data: [] }) // JSONP 跨域(老项目用)
// 设置状态码
res.status(201).json({ id: 123 })
res.status(404).send('Not Found')
res.status(500).send('Server Error')
// 重定向
res.redirect('/login') // 302 临时重定向
res.redirect(301, '/new-url') // 301 永久重定向
res.redirect('https://example.com') // 外部重定向
res.redirect('back') // 返回上一页(Referer 头)
// 文件下载
res.download('./report.pdf') // 作为附件下载
res.download('./report.pdf', 'custom-name.pdf') // 自定义文件名
res.download('./report.pdf', (err) => { }) // 完成后回调
// 设置响应头
res.set('Content-Type', 'application/json')
res.set({
'X-Custom-Header': 'value',
'Cache-Control': 'no-cache'
})
// 一次性设置方法别名
res.type('json') // 等同于 set('Content-Type', 'application/json')
res.status(404)
// 追加 Cookie(需配合 cookie-parser)
res.cookie('token', 'abc123', {
httpOnly: true, // 禁止 JS 读取(防 XSS)
secure: false, // HTTPS 下才传
sameSite: 'strict', // 防 CSRF
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 天有效期(毫秒)
path: '/', // 生效路径
domain: '.example.com' // 生效域名
})
res.clearCookie('token') // 清除 Cookie
// 结束响应(不再发送数据)
res.end()
// 渲染模板(需配合模板引擎)
res.render('index', { title: 'Home' })
中间件
app.use([path], ...handlers)
注册中间件,匹配路径下的所有请求。
// 全局中间件(所有请求)
app.use((req, res, next) => {
console.log(req.path)
next() // 必须调用 next() 继续后续处理
})
// 路径限定
app.use('/api', (req, res, next) => {
// 只有 /api 开头的请求进入
next()
})
// 多中间件(按顺序执行)
app.use((req, res, next) => {
req.startTime = Date.now()
next()
}, (req, res, next) => {
console.log(`耗时: ${Date.now() - req.startTime}ms`)
next()
})
// 错误处理中间件(4 个参数)
app.use((err, req, res, next) => {
console.error(err.stack)
res.status(500).json({ error: 'Internal Error' })
})
路由参数预处理
app.param(names, handler)
当路由参数匹配时自动触发,常用于 ID 校验、权限检查。
// 匹配任意路由的 id 参数
app.param('id', (req, res, next, id) => {
req.id = parseInt(id, 10)
next()
})
// 多个参数
app.param(['id', 'uid'], (req, res, next, id) => {
next()
})
// 注意:3.x 语法不同,handlers 写法变了
模板引擎(内置)
app.set(name, value)
设置应用级配置。
app.set('port', 3000) // process.env.PORT || 3000
app.set('env', 'production') // 环境:development / production
app.set('json spaces', 2) // 开发时美化 JSON 输出
app.set('trust proxy', 1) // 反向代理场景:信任第 1 层代理
app.set('view engine', 'ejs') // 模板引擎
app.set('views', './views') // 模板目录
错误处理
自定义错误类 + 全局处理
class ApiError extends Error {
constructor(status, message) {
super(message)
this.status = status
}
}
app.use((err, req, res, next) => {
if (err instanceof ApiError) {
return res.status(err.status).json({ error: err.message })
}
res.status(500).json({ error: '服务器错误' })
})
// 抛出错误
app.get('/users/:id', (req, res, next) => {
if (!user) return next(new ApiError(404, '用户不存在'))
res.json(user)
})
应用级方法
// 监听端口启动服务
app.listen(3000, () => {
console.log('Server running on http://localhost:3000')
})
// 返回原生 http.Server 实例
const server = app.listen(3000)
server.close()
// 应用级配置
app.set('port', 3000)
app.enable('trust proxy') // 等同于 set('trust proxy', true)
app.disable('etag') // 关闭某个特性
// 检查特性开关
app.enabled('trust proxy') // true
// 路由路径匹配(内部用)
app.match(req, path)
// 判断是否已挂载
app.path() // 返回应用路径前缀
// 获取所有路由
app._router.stack.forEach(item => {
if (item.route) {
console.log(item.route.path, Object.keys(item.route.methods))
}
})
常用第三方中间件组合
const express = require('express')
const cors = require('cors')
const helmet = require('helmet')
const morgan = require('morgan')
const compression = require('compression')
const cookieParser = require('cookie-parser')
const expressRateLimit = require('express-rate-limit')
const { expressjwt } = require('express-jwt')
const joi = require('joi')
const multer = require('multer')
const sharp = require('sharp')
const app = express()
// 安全
app.use(helmet()) // 安全响应头
app.use(cors({ origin: '*', credentials: true })) // CORS
// 日志
app.use(morgan('combined')) // 请求日志
// 性能
app.use(compression()) // gzip 压缩
// 解析
app.use(express.json({ limit: '10mb' }))
app.use(express.urlencoded({ extended: true }))
app.use(cookieParser('secret'))
// 限流
app.use('/api', expressRateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
message: { error: '请求过于频繁' }
}))
// 鉴权
app.use(expressjwt({ secret: 'secret', algorithms: ['HS256'] }))
// 文件上传
const upload = multer({ dest: 'uploads/' })
app.post('/upload', upload.single('file'), (req, res) => {
console.log(req.file) // 文件信息
console.log(req.body) // 其他字段
})
// 路由
app.use('/api/users', require('./routes/users'))
app.listen(3000)
路由组织最佳实践
routes/
index.js // app.use('/', require('./routes/index'))
users.js
articles.js
middleware/
auth.js
errorHandler.js
controllers/
userController.js
models/
userModel.js
// routes/users.js
const express = require('express')
const router = express.Router()
const { getUsers, getUser, createUser, updateUser, deleteUser } = require('../controllers/userController')
router.get('/', getUsers)
router.get('/:id', getUser)
router.post('/', createUser)
router.put('/:id', updateUser)
router.delete('/:id', deleteUser)
module.exports = router
常见问题
| 问题 | 解决方案 |
|---|---|
req.body 为 {} |
未使用 express.json() 或 express.urlencoded() |
req.params 为 {} |
路由路径参数名写错了(如 :id 但实际访问路径不符) |
| CORS 跨域 | app.use(cors()) |
| 中文乱码 | res.set('Content-Type', 'text/html; charset=utf-8') |
| 请求日志 | morgan('dev') 或 morgan('combined') |
| 静态文件 404 | express.static 路径写错了(相对路径基于启动目录) |
| 异步错误未捕获 | 在异步路由中 try/catch + next(err) |
| 生产环境配置 | NODE_ENV=production node app.js |