Express 什么是中间件
简单理解中间件就是:Express调用的函数;函数调用时会被 Express 传入 3 个参数:req、res、next。参数与我们上篇文章讲路由的参数一致。
Express 认识中间件
在 Express 中,中间件(Middleware) 是核心机制之一,它可以拦截请求和响应的处理流程,对请求数据进行处理、修改响应行为,或执行通用逻辑(如日志记录、权限验证、数据解析等)。理解中间件的工作原理和使用场景,是掌握 Express 开发的关键。
中间件函数可以做什么
- 它可以执行任何代码;
- 更改
req、res对象; - 结束请求响应周期;
- 调用
stack栈中的下一个中间件;
中间件的执行机制
- 匹配上的中间件会按照顺序执行;
- 一次只执行一个中间件,如果要执行下一个匹配上的中间件,调用
next(); - 如果当前中间件没有结束请求响应周期,必须调用
next();
中间件分类
- App 中间件
- Router 中间件
- Express 内置中间件
- 第三方中间件
- 错误处理中间件
比较特殊的中间件,上方提到了中间件有 3 个参数,它有 4 个参数
APP中间件
-
use 中间件
将指定的中间件函数或多个函数挂载到指定路径:当请求路径的基础部分与指定路径匹配时,中间件函数就会被执行。
- 使用
app.use()方法将中间件提供给 Express 调用 use()方法的第一个参数默认就是'/',跟路径可省略use()方法可以匹配所有HTTP方法
import express from 'express'; import ejs from 'ejs'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; import htmlRouter from './router/html.js'; import userRouter from './router/api/user.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const app = express(); const port = 3000; app.use(express.static(join(__dirname, 'public'))); app.set('views', join(__dirname, '/template')); app.set('view engine', 'html'); app.engine('html', ejs.__express); // use() 方法的第一个参数默认就是'/',跟路径可省略;并匹配所有 HTTP 方法 app.use((req, res, next) => { console.log(1, `${req.method} ${req.url}`); next() // 执行下一个匹配上的中间件 }) // 也可以在一个 use() 方法中注册多个中间件 app.use( '/', (req, res, next) => { console.log(2, `${req.method} ${req.url}`); next() // 执行下一个匹配上的中间件 }, (req, res, next) => { console.log(3, `${req.method} ${req.url}`); next() // 执行下一个匹配上的中间件 }, ) // 当路径是 ‘/’ 返回 html app.use('/', htmlRouter); // 当路径是 ‘/’ 时匹配不上,所以不会被执行 // 当路径是 ‘/user’ 时候被执行,上方的匹配 ‘/’ 匹配也都会被执行 app.use('/user', userRouter); app.listen(port, () => { console.log(`Server is running on port ${port}`); }); - 使用
-
METHOD 和 all 中间件
路由一个 HTTP 请求,其中 METHOD 是请求的 HTTP 方法,如 GET、PUT、POST 等,需为小写形式。因此,实际的方法有
app.get()、app.post()、app.put()等。METHOD和all方法是路径相等才会进行匹配all()方法会被所有 HTTP 方法所执行
import express from 'express'; import ejs from 'ejs'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; import htmlRouter from './router/html.js'; import userRouter from './router/api/user.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const app = express(); const port = 3000; app.use(express.static(join(__dirname, 'public'))); app.set('views', join(__dirname, '/template')); app.set('view engine', 'html'); app.engine('html', ejs.__express); // 模拟 get 请求 // 被匹配 get 请求并执行 app.get('/', (req, res, next) => { console.log(1, `${req.method} ${req.url}`); next() }) // 非 get 请求不会被执行 app.post('/', (req, res, next) => { console.log(1, `${req.method} ${req.url}`); next() }) // all() 方法 会被所有 HTTP 方法匹配,所以会执行 app.all('/', (req, res, next) => { console.log(2, `${req.method} ${req.url}`); next() }) // 路径不相等,不会被执行 app.all('/user/list', (req, res, next) => { console.log(2, `${req.method} ${req.url}`); next() }) app.use('/', htmlRouter); app.use('/user', userRouter); app.listen(port, () => { console.log(`Server is running on port ${port}`); });next()我们也可以使用next('route')传递 route 跳出当前匹配执行一下条路由匹配内容
import express from 'express'; import ejs from 'ejs'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; import htmlRouter from './router/html.js'; import userRouter from './router/api/user.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const app = express(); const port = 3000; app.use(express.static(join(__dirname, 'public'))); app.set('views', join(__dirname, '/template')); app.set('view engine', 'html'); app.engine('html', ejs.__express); app.get('/', (req, res, next) => { console.log(1, `${req.method} ${req.url}`); // 执行 next('route') 会结束当前匹配,当前匹配下方的匹配规则 console.log(2, ...)将不会被执行 next('route'); }, (req, res, next) => { console.log(2, `${req.method} ${req.url}`); next(); }, ) app.use('/', htmlRouter); app.use('/user', userRouter); app.listen(port, () => { console.log(`Server is running on port ${port}`); });
Router中间件
- use 中间件
- METHOD 和 all 中间件
use、METHOD、all 中间件与 app中间件使用方法一致,不做代码示例了。把 app 换成自己创建的路由即可
Express 内置中间件
urlencoded([options]):处理请求体 application/x-www-form-urlencoded 格式的数据。
import express from 'express';
import {urlencoded} from "express";
app.use(urlencoded({ extended: true }));
json([options]):处理请求体 application/json 格式的数据。
import express from 'express';
import {json} from "express";
app.use(json());
static(root, [options]):提供静态文件资源
import express from 'express';
// static as staticMiddleware 表示给方法体内的 static 方法起一个别名叫 staticMiddleware
import {urlencoded, json, static as staticMiddleware} from "express";
import {dirname, join} from 'path';
// 获取当前模块的文件路径
const __filename = fileURLToPath(import.meta.url);
// 获取当前模块所在的目录路径
const __dirname = dirname(__filename);
// 对外暴漏 public 文件夹,可通过URL 访问到内部资源,如:图片、样式、js、html 等
app.use(staticMiddleware(join(__dirname, 'public')));
Express 第三方中间件 cookie-parser
cookie-parser 是一个 Node.js 中间件,常用于 Express 应用中解析 HTTP 请求里的 Cookie 头信息。它能把 Cookie 字符串转换为 JavaScript 对象,方便开发者在应用里访问和操作客户端发送的 Cookie。下面详细介绍其安装、使用第三方中间件的方法、配置和示例。
在项目根目录下安装中间件
npm install cookie-parser
使用方法
在 Express 应用里引入并使用 cookie-parser 中间件,它会将解析后的 Cookie 挂载到 req.cookies 对象上。若提供了密钥,还能解析签名 Cookie 到 req.signedCookies 对象。
import express from 'express';
import cookieParser from 'cookie-parser';
const app = express();
const port = 3000;
// 使用 cookie-parser 中间件,可传入密钥用于解析签名 Cookie
app.use(cookieParser('your-secret-key'));
// 设置 Cookie 的路由
app.get('/set-cookie', (req, res) => {
// 设置一个普通 Cookie
res.cookie('username', 'JohnDoe', { maxAge: 900000, httpOnly: true });
// 设置一个签名 Cookie
res.cookie('signedCookie', 'signedValue', { signed: true });
res.send('Cookies have been set');
});
// 获取 Cookie 的路由
app.get('/get-cookie', (req, res) => {
// 获取普通 Cookie
const username = req.cookies.username;
// 获取签名 Cookie
const signedValue = req.signedCookies.signedCookie;
res.json({ username, signedValue });
});
// 删除 Cookie 的路由
app.get('/delete-signed-cookie', (req, res) => {
// 删除名为 signedCookie 的签名 Cookie
res.clearCookie('signedCookie', { signed: true });
res.send('Signed cookie has been deleted');
});
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
错误处理中间件
什么是错误处理中间件?错误处理中间件是一种特殊的中间件,用于捕获和处理应用程序中出现的同步或异步错误,并统一处理错误响应(如返回错误状态码、错误信息等)。它是 Express 错误处理机制的核心,能避免未处理的错误导致应用崩溃,同时让错误处理逻辑更集中、规范。
错误处理中间件的特点
参数要求: 与普通中间件不同,错误处理中间件必须包含 4 个参数:(err, req, res, next),缺一不可。 err:捕获到的错误对象,其余三个参数与中间件一致
注册顺序: 必须注册在 所有路由中间件和普通中间件之后,否则无法捕获前面中间件抛出的错误。
访问 用户路由 代码示例:
import express from 'express';
import {urlencoded, json, static as staticMiddleware} from "express";
import ejs from 'ejs';
import {fileURLToPath} from 'url';
import {dirname, join} from 'path';
import htmlRouter from './router/html.js';
import userRouter from './router/api/user.js';
const app = express();
const port = 3000;
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
app.set('views', join(__dirname, '/template'));
app.set('view engine', 'html');
app.engine('html', ejs.__express);
app.use(staticMiddleware(join(__dirname, 'public')));
app.use(urlencoded({ extended: true }));
app.use(json());
// 页面路由
app.use('/', htmlRouter);
// 是否登录中间件
const isLongin = (req, res, next) => {
// 当前用户是否登录
if (req.cookies.token) {
// 是否过期
if (isExpired) {
res.json({code: '401', msg: '登录过期,请重新登录'});
} else {
next();
}
} else {
// 未登录,问题反馈给错误处理中间件
next(new Error('用户未登录'));
}
};
// 用户路由
app.use('/user', isLongin, userRouter);
// 错误处理中间件
app.use((err, req, res, next) => {
// 默认为 500 错误,若状态码存在则使用自定义状态码
const statusCode = err.statusCode || 500;
// 接收到错误处理并执行返回相对应的 json
res.status(statusCode).json({
code: '-1',
msg: err.message,
});
})
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});