• 微信:WANCOME
  • 扫码加微信,提供专业咨询
  • 服务热线
  • 13215191218
    13027920428

  • 微信扫码访问本页
desc-2
环企首页

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 方法的路由,支持 getpostputpatchdeleteheadoptions

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