Express(二) ——中间件
在 Express 中,中间件是一个可以访问请求对象、响应对象和调用 next 方法的一个函数。
1. 简单例子(打印请求日志)
一个 Express 应用,就是由许许多多的中间件来完成的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| const express = require("express");
const app = express();
app.use((req, res, next) => { console.log(`请求日志:${req.method} ${req.url} ${new Date()}`); next(); });
app.get("/", (req, res) => { res.send("get / "); });
app.post("/", (req, res) => { res.send("post / "); });
app.delete("/", (req, res) => { res.send("delete /"); });
const port = 3000;
app.listen(port, () => { console.log(`http://localhost:${port}/`); });
|
可以发现:任何请求进来都会先打印请求日志,然后才会执行具体的业务处理函数
2. 中间件的组成
1 2 3 4 5
| app.use("/", (req, res, next) => { next(); });
|
如果当前中间件没有结束请求相应周期,则需要通过 next()调用下一个中间件,否则,该请求将会被挂起
3. 中间件功能
- 执行任何代码
- 修改 request 或 response 对象
- 结束请求响应周期
- 调用下一个中间件
4. 中间件分类
4.1 应用程序级别中间件
4.1.1 不做任何限定的中间件
即所有请求都会通过该中间件
1 2 3
| app.use(function (req, res, next) { res.send("不做任何限定的中间件"); });
|
4.1.2 限定请求路径
即只有请求路径匹配才会通过该中间件
1 2 3
| app.use("/user/:id", function (req, res, next) { res.send("限定请求路径的中间件"); });
|
4.1.3 限定请求方法 + 请求路径
不能只限定请求方法,因为 app.get()第一个参数必须
1 2 3
| app.get("/", (req, res) => { res.send("限定请求方法 + 请求路径的中间件"); });
|
4.1.4 多个处理函数
1 2 3 4 5 6 7 8 9 10
| app.use( function (req, res, next) { console.log("第一次处理"); next(); }, function (req, res, next) { console.log("第二次处理"); next(); } );
|
也可以通过回调函数数组
1 2 3 4 5 6 7 8 9 10 11
| const first = (req, res, next) => { console.log("第一次处理"); next(); };
const second = (req, res, next) => { console.log("第二次处理"); next(); };
app.use([first, second]);
|
4.1.5 多个路由处理函数
1 2 3 4 5 6 7 8
| app.use("/", (req, res, next) => { console.log("第一个路由处理函数"); next(); }); app.use("/", (req, res, next) => { console.log("第二个路由处理函数"); res.end(); });
|
4.1.6 显示一个中间件子堆栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| app.get( "/:id", function (req, res, next) { if (req.params.id === "0") { next("route"); } else { next(); } }, function (req, res, next) { res.send("regular"); } );
app.get("/:id", function (req, res, next) { res.send("special"); });
|
上面例子中,动态参数 id 不为 0 时,第一个处理函数会调用 next(),然后会执行第二个处理函数,然后会收到 regular 的响应。而当 id 为 0 时,会调用 next(‘route’),会跳过当前堆栈之后的所有中间件。即不执行后面的处理函数,而是直接去执行后面的路由处理函数。所以不会打印 regular,而是打印 special。
4.2 路由级别中间件
router.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const express = require("express");
const router = express.Router();
router.get("/aaa", (req, res) => { res.send("get /aaa"); });
router.post("/bbb", (req, res) => { res.send("post /bbb"); });
module.exports = router;
|
app.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const express = require("express"); const app = express();
const router = require("./router.js");
app.get("/", (req, res) => { res.send("Hello World!"); });
app.use("/abc", router);
app.listen(3000, () => { console.log("http://localhost:3000/"); });
|
链式路由处理程序:
1 2 3 4 5 6 7 8
| app .route("/abc") .get((req, res) => { res.send("get"); }) .post((req, res) => { res.send("post"); });
|
4.3 错误处理中间件
1 2 3 4 5 6 7
| app.use((err, req, res, next) => { console.log("错误: ", err); res.status(500).json({ error: err.message, }); });
|
四个参数都要有才是错误处理中间件。如果只有 err、req、res,则 err 实际上是 req 对象
app.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const express = require("express"); const app = express();
const router = require("./router.js");
app.use(router);
app.use((err, req, res, next) => { console.log("错误: ", err); res.status(500).json({ error: err.message, }); });
app.listen(3000, () => { console.log("http://localhost:3000/"); });
|
router.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const express = require("express");
const router = express.Router();
router.get("/", (req, res, next) => { try { const d = b + 1; } catch (err) { next(err); } });
router.get("/", (req, res, next) => { console.log("第二个路由处理函数"); res.end(); });
module.exports = router;
|
4.4 内置中间件
- **express.json()**:解析 Content-Type 为
application/json
格式的请求体
- **express.urlencoded()**:解析 Content-Type 为
application/x-www-form-urlencoded
格式的请求体
- **express.raw()**:解析 Content-Type 为
application/octet-stream
格式的请求体
- **express.text()**:解析 Content-Type 为
text/plain
格式的请求体
- **express.static()**:托管静态资源文件
4.5 第三方中间件
Express middleware
使用示例:morgan 日志中间件
npm install morgan
const express = require("express");
const morgan = require("morgan"); // 1. 引入
const app = express();
app.use(morgan("tiny")); // 2. 挂载
app.get("/", (req, res) => {
res.send("Hello World!");
});
app.listen(3000, () => {
console.log("http://localhost:3000/");
});
每次请求都会打印出请求日志