今天重点就是三个章节: 路由,中间件,自定义中间件。
本章节主要讲解 路由及其模块化。
路由就是映射关系。请求方式和资源路径与处理函数中间的对应关系。所以路由分 3 部分组成,分别是请求的类型、请求的 URL 地址、和处理函数。
图示:

express 中的简单路由书写形式为:
x// 安装 express// npm i express@4.17.1// express创建服务const express = require("express");const app = express();// 定义简单路由app.get("/", (req, res) => { res.send("get 类型请求, 路径为 / ;")})app.post("/", (req, res) => { res.send("post 类型请求, 路径为 / ;")})// 监听端口app.listen(80, () => { console.log("http://localhost")})简单路由书写简单,使用方便,但是如果项目比较大,路由较多,难于处理和维护。所以为了处理和维护方便,express推荐我们讲相同模块中的路由抽离到一个文件中,示例:
xxxxxxxxxx// 创建路由模块const express = require("express");// 创建路由const router = express.Router();// 创建各种路由对应的逻辑router.get('/user/list', (req, res) => { res.send("get请求, 获取所有用户信息!!!")});router.post('/user/add', (req, res) => { res.send("POST请求, 添加用户!")});// 把路由对象导出module.exports = router;导入路由模块,挂载路由:
xxxxxxxxxxconst express = require("express");const app = express();// 1. 抽离路由模块, 并引入const router = require("./03.router");// 2. 挂载路由(注册路由,让路由生效)// // 2.1 简单挂载// app.use(router);// 2.2 挂在前缀// 注:app.use()用于注册全局中间件函数;app.use("/api", router);// 监听端口app.listen(80, () => { console.log("http://localhost")})本章节主要讲解:中间件的概念和作用,中间件的格式,全局中间件和局部中间件,五类中间件;
中间件(Middleware ),特指业务流程的中间处理环节。当一个请求到达 Express 的服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理。
使用中间件的作用就是处理请求。对一部分请求直接回复响应,对于另一部分请求,可以稍作处理后,交由后续逻辑继续处理。例如:静态资源开放中间件,POST参数处理中间件,身份认证中间件等。

中间件,本质上就是一个 function 处理函数,Express 中间件的格式如下:

注意:中间件函数的形参列表中,必须包含 next 参数。而路由处理函数中只包含 req 和 res。
中间件分为全局中间件和局部中间件。全局中间件就是针对所有路由全部生效的中间件,局部中间件只针对某个路由起作用,其他的不受影响。
代码演示:
xxxxxxxxxxconst express = require("express");const app = express();// 全局处理中间件 - 要写到路由的上面const mw = (req, res, next) => { console.log("全局处理中间件") next();}app.use(mw);// 路由app.get("/", (req, res) => { // 挂载属性 req.time = Date.now() res.send("首页");})// 监听端口app.listen(80, () => { console.log("http://localhost")})代码演示:
xxxxxxxxxx// 创建路由模块const express = require("express");// 创建路由const router = express.Router();// 局部处理中间件1const mw1 = (req, res, next) => { console.log("局部处理中间件1") next();}// 局部处理中间件2const mw2 = (req, res, next) => { console.log("局部处理中间件2") next();}// 多个中间件可以单个传递:mv1,mv2,mv3... 也可以数组传递:[mv1,mv2,mv3]// router.get('/user/list', [mw1, mw2], (req, res) => {router.get('/user/list', mw1, mw2, (req, res) => { console.log("get 请求, 路径 /user/list"); res.send("get请求, 获取所有用户信息!!!" + req.time)});router.post('/user/add', (req, res) => { console.log("post 请求, 路径 /user/add"); res.send("POST请求, 添加用户!" + req.time)});// 把路由对象导出module.exports = router;注意:中间件之间共享req和res对象。前面的中间件,向 req 和 res 对象上绑定的属性和方法,后面的中间件和路由中可以自由获取并使用这些属性和方法;(上面案例中的 req.time 就是示例)
为了方便大家理解和记忆中间件的使用,Express 官方把常见的中间件用法,分成了 5 大类,分别是:
① 应用级别的中间件
② 路由级别的中间件
③ 错误级别的中间件
④ Express 内置的中间件
⑤ 第三方的中间件
由开发人员自己定义的中间件,有具体的功能需求,视具体需求而定:
xxxxxxxxxx// 此处中间件,向res对象上绑定了一个 cc(), 后续的路由及中间件就可以通过res对象调用此方法了app.use((req, res, next) => { // status 默认值为 1,表示失败的情况 // err 的值,可能是一个错误对象,也可能是一个错误的描述字符串 res.cc = function (err, status = 1) { res.send({ status, message: err instanceof Error ? err.message : err, }) } next()})简单路由以及模块化路由都属于路由基本中间件,代码略。
用于错误处理的中间件。
xxxxxxxxxxconst express = require("express");const app = express();// 定义简单路由app.get("/", (req, res) => { throw new Error("模拟服务器的错误!") // const num1 = 111; // num1 = 222; res.send("get 类型请求, 路径为 / ;")})// 错误处理中间件app.use((err, req, res, next) => { res.send({ status: 501, message: err.message })})// 监听端口app.listen(80, () => { console.log("http://localhost")})express内置的中间件,express.static(),express.json(),express.urlencoded({extended: false})等。
xxxxxxxxxxconst express = require("express");const app = express();// express 内置中间件: 开放静态资源app.use(express.static("./clock"))// express 内置中间件: json 格式app.use(express.json())// express 内置中间件: a=1&b=2 格式app.use(express.urlencoded({extended: false}));// 定义路由app.post("/user", (req, res) => { // 获取 json 格式 或者 a=1&b=2 格式的post参数 console.log(req.body) res.send("post 类型请求, 路径为 /user ;")})// 监听端口app.listen(80, () => { console.log("http://localhost")})由开发人员自己定义的中间件,有具体的功能需求,视具体需求而定:
xxxxxxxxxxconst express = require("express");const app = express();// 1.下载: // npm install body-parser// 2.引入const bodyParser = require("body-parser")// 3.使用第三方中间件: a=1&b=2 格式app.use(bodyParser.urlencoded({ extended: false }))// 定义路由app.post("/user", (req, res) => { // 获取第三方中间件操作后,接收到的post参数 // express.urlencoded({extended: false})的底层原理 console.log(req.body) res.send("post 类型请求, 路径为 /user ;")})// 监听端口app.listen(80, () => { console.log("http://localhost")})类似应用基本的中间件,但是经过模块化封装后,可以复用。
xxxxxxxxxx// 文件名:10.1.myself-body-parser// 导入核心功能模块const querystring = require("querystring")// 定义中间件函数const bodyParser = (req, res, next)=>{ // 0.定义一个累计变量 var str = ""; // 1.data: 只要客户端发送数据,就会触动这个事件 req.on("data", chunk => { str += chunk; }) // 2.end: 一次请求报文发送完毕之后, 才会触动这个事件; req.on("end", () => { req.body = querystring.parse(str); next(); })}// 导出module.exports = bodyParser;使用自定义中间件:
xxxxxxxxxxconst express = require("express");const app = express();// 07.使用中间件const bodyParser = require("./10.1.myself-body-parser");app.use(bodyParser)// 定义一个post路由app.post("/user", (req, res) => { res.send(req.body);})// 监听端口app.listen(80, () => { console.log("http://localhost")})