使用中间件
Express 是一款路由与中间件 Web 框架,自身仅提供极简的基础功能:一个 Express 应用本质上就是一连串中间件函数的调用。
Middleware functions are functions that have access to the request object (req), the response object (res), and the next middleware function in the application’s request-response cycle. 下一个中间件函数通常使用名为next的变量来表示。
中间件函数可执行下列任务:
- 执行任意代码。
- 修改请求对象与响应对象。
- 终止请求-响应周期。
- 调用栈中的下一个中间件函数。
如果当前中间件函数没有终止请求-响应周期,它必须调用 next() 以将控制权传递给下一个中间件函数。 否则,请求将会被挂起。
Express 应用可以使用以下类型的中间件:
- Application-level middleware
- Router-level middleware
- Error-handling middleware
- Built-in middleware
- Third-party middleware
你可以通过可选的挂载路径加载应用级中间件和路由级中间件。 你也可以同时加载一系列中间件函数,这会在挂载点处创建中间件系统的一个子栈。
应用级中间件
Bind application-level middleware to an instance of the app object by using the app.use() and app.METHOD() functions, where METHOD is the HTTP method of the request that the middleware function handles (such as GET, PUT, or POST) in lowercase.
本示例展示了一个没有挂载路径的中间件函数。 每当应用接收请求时,该函数都会被执行。
const express = require('express');const app = express();
app.use((req, res, next) => { console.log('Time:', Date.now()); next();});import express from 'express';
const app = express();
app.use((req, res, next) => { console.log('Time:', Date.now()); next();});import express, { type Express, type Request, type Response, type NextFunction } from 'express';
const app: Express = express();
app.use((req: Request, res: Response, next: NextFunction) => { console.log('Time:', Date.now()); next();});本示例展示了一个挂载在 /user/:id 路径上的中间件函数。 只要是发往 /user/:id 路径的任意类型 HTTP 请求,都会执行该中间件函数。
app.use('/user/:id', (req, res, next) => { console.log('Request Type:', req.method); next();});import { type Request, type Response, type NextFunction } from 'express';
app.use('/user/:id', (req: Request, res: Response, next: NextFunction) => { console.log('Request Type:', req.method); next();});该示例展示了一条路由及其处理函数(中间件体系)。 该函数处理指向 /user/:id 路径的 GET 请求。
app.get('/user/:id', (req, res, next) => { res.send('USER');});import { type Request, type Response, type NextFunction } from 'express';
app.get('/user/:id', (req: Request, res: Response, next: NextFunction) => { res.send('USER');});下面是在指定挂载路径的挂载点加载一系列中间件函数的示例。
该示例演示了一个中间件子栈,会对所有访问 /user/:id 路径的各类 HTTP 请求打印请求信息。
app.use( '/user/:id', (req, res, next) => { console.log('Request URL:', req.originalUrl); next(); }, (req, res, next) => { console.log('Request Type:', req.method); next(); });import { type Request, type Response, type NextFunction } from 'express';
app.use( '/user/:id', (req: Request, res: Response, next: NextFunction) => { console.log('Request URL:', req.originalUrl); next(); }, (req: Request, res: Response, next: NextFunction) => { console.log('Request Type:', req.method); next(); });路由处理程序支持为同一个路径定义多条路由。 下面的示例为 /user/:id 路径的 GET 请求定义了两条路由。 第二条路由不会报错,但永远不会被执行,因为第一条路由已经结束了请求-响应周期。
该示例展示了一个中间件子栈,用于处理 /user/:id 路径的 GET 请求。
app.get( '/user/:id', (req, res, next) => { console.log('ID:', req.params.id); next(); }, (req, res, next) => { res.send('User Info'); });
// handler for the /user/:id path, which prints the user IDapp.get('/user/:id', (req, res, next) => { res.send(req.params.id);});import { type Request, type Response, type NextFunction } from 'express';
app.get( '/user/:id', (req: Request, res: Response, next: NextFunction) => { console.log('ID:', req.params.id); next(); }, (req: Request, res: Response, next: NextFunction) => { res.send('User Info'); });
// handler for the /user/:id path, which prints the user IDapp.get('/user/:id', (req: Request, res: Response, next: NextFunction) => { res.send(req.params.id);});若要跳过路由中间件栈中剩余的中间件函数,调用 next('route') 即可将控制权传递给下一条路由。
Note
next('route') 仅在通过 app.METHOD() 或 router.METHOD() 函数加载的中间件函数中生效。
该示例展示了一个中间件子栈,用于处理 /user/:id 路径的 GET 请求。
app.get( '/user/:id', (req, res, next) => { // if the user ID is 0, skip to the next route if (req.params.id === '0') next('route'); // otherwise pass the control to the next middleware function in this stack else next(); }, (req, res, next) => { // send a regular response res.send('regular'); });
// handler for the /user/:id path, which sends a special responseapp.get('/user/:id', (req, res, next) => { res.send('special');});import { type Request, type Response, type NextFunction } from 'express';
app.get( '/user/:id', (req: Request, res: Response, next: NextFunction) => { // if the user ID is 0, skip to the next route if (req.params.id === '0') next('route'); // otherwise pass the control to the next middleware function in this stack else next(); }, (req: Request, res: Response, next: NextFunction) => { // send a regular response res.send('regular'); });
// handler for the /user/:id path, which sends a special responseapp.get('/user/:id', (req: Request, res: Response, next: NextFunction) => { res.send('special');});中间件也可以定义在数组中,实现复用。
该示例展示了一个使用数组封装的中间件子栈,用于处理 /user/:id 路径的 GET 请求。
function logOriginalUrl(req, res, next) { console.log('Request URL:', req.originalUrl); next();}
function logMethod(req, res, next) { console.log('Request Type:', req.method); next();}
const logStuff = [logOriginalUrl, logMethod];app.get('/user/:id', logStuff, (req, res, next) => { res.send('User Info');});import { type Request, type Response, type NextFunction } from 'express';
function logOriginalUrl(req: Request, res: Response, next: NextFunction) { console.log('Request URL:', req.originalUrl); next();}
function logMethod(req: Request, res: Response, next: NextFunction) { console.log('Request Type:', req.method); next();}
const logStuff = [logOriginalUrl, logMethod];app.get('/user/:id', logStuff, (req: Request, res: Response, next: NextFunction) => { res.send('User Info');});路由级中间件
路由级中间件的工作方式与应用级中间件完全相同,唯一区别是它绑定到 express.Router() 的实例上。
const router = express.Router();import express from 'express';
const router = express.Router();通过 router.use() 和 router.METHOD() 函数加载路由级中间件。
下面的示例代码通过路由级中间件,复刻了上方展示的应用级中间件的中间件体系:
const express = require('express');const app = express();const router = express.Router();
// a middleware function with no mount path. This code is executed for every request to the routerrouter.use((req, res, next) => { console.log('Time:', Date.now()); next();});
// a middleware sub-stack shows request info for any type of HTTP request to the /user/:id pathrouter.use( '/user/:id', (req, res, next) => { console.log('Request URL:', req.originalUrl); next(); }, (req, res, next) => { console.log('Request Type:', req.method); next(); });
// a middleware sub-stack that handles GET requests to the /user/:id pathrouter.get( '/user/:id', (req, res, next) => { // if the user ID is 0, skip to the next router if (req.params.id === '0') next('route'); // otherwise pass control to the next middleware function in this stack else next(); }, (req, res, next) => { // render a regular page res.render('regular'); });
// handler for the /user/:id path, which renders a special pagerouter.get('/user/:id', (req, res, next) => { console.log(req.params.id); res.render('special');});
// mount the router on the appapp.use('/', router);import express from 'express';
const app = express();const router = express.Router();
// a middleware function with no mount path. This code is executed for every request to the routerrouter.use((req, res, next) => { console.log('Time:', Date.now()); next();});
// a middleware sub-stack shows request info for any type of HTTP request to the /user/:id pathrouter.use( '/user/:id', (req, res, next) => { console.log('Request URL:', req.originalUrl); next(); }, (req, res, next) => { console.log('Request Type:', req.method); next(); });
// a middleware sub-stack that handles GET requests to the /user/:id pathrouter.get( '/user/:id', (req, res, next) => { // if the user ID is 0, skip to the next router if (req.params.id === '0') next('route'); // otherwise pass control to the next middleware function in this stack else next(); }, (req, res, next) => { // render a regular page res.render('regular'); });
// handler for the /user/:id path, which renders a special pagerouter.get('/user/:id', (req, res, next) => { console.log(req.params.id); res.render('special');});
// mount the router on the appapp.use('/', router);import express, { type Express, type Request, type Response, type NextFunction } from 'express';
const app: Express = express();const router = express.Router();
// a middleware function with no mount path. This code is executed for every request to the routerrouter.use((req: Request, res: Response, next: NextFunction) => { console.log('Time:', Date.now()); next();});
// a middleware sub-stack shows request info for any type of HTTP request to the /user/:id pathrouter.use( '/user/:id', (req: Request, res: Response, next: NextFunction) => { console.log('Request URL:', req.originalUrl); next(); }, (req: Request, res: Response, next: NextFunction) => { console.log('Request Type:', req.method); next(); });
// a middleware sub-stack that handles GET requests to the /user/:id pathrouter.get( '/user/:id', (req: Request, res: Response, next: NextFunction) => { // if the user ID is 0, skip to the next router if (req.params.id === '0') next('route'); // otherwise pass control to the next middleware function in this stack else next(); }, (req: Request, res: Response, next: NextFunction) => { // render a regular page res.render('regular'); });
// handler for the /user/:id path, which renders a special pagerouter.get('/user/:id', (req: Request, res: Response, next: NextFunction) => { console.log(req.params.id); res.render('special');});
// mount the router on the appapp.use('/', router);若要跳过当前路由实例中剩余的中间件函数,调用 next('router') 即可将控制权交回给路由实例。
该示例展示了一个中间件子栈,用于处理 /user/:id 路径的 GET 请求。
const express = require('express');const app = express();const router = express.Router();
// predicate the router with a check and bail out when neededrouter.use((req, res, next) => { if (!req.headers['x-auth']) return next('router'); next();});
router.get('/user/:id', (req, res) => { res.send('hello, user!');});
// use the router and 401 anything falling throughapp.use('/admin', router, (req, res) => { res.sendStatus(401);});import express from 'express';
const app = express();const router = express.Router();
// predicate the router with a check and bail out when neededrouter.use((req, res, next) => { if (!req.headers['x-auth']) return next('router'); next();});
router.get('/user/:id', (req, res) => { res.send('hello, user!');});
// use the router and 401 anything falling throughapp.use('/admin', router, (req, res) => { res.sendStatus(401);});import express, { type Express, type Request, type Response, type NextFunction } from 'express';
const app: Express = express();const router = express.Router();
// predicate the router with a check and bail out when neededrouter.use((req: Request, res: Response, next: NextFunction) => { if (!req.headers['x-auth']) return next('router'); next();});
router.get('/user/:id', (req: Request, res: Response) => { res.send('hello, user!');});
// use the router and 401 anything falling throughapp.use('/admin', router, (req: Request, res: Response) => { res.sendStatus(401);});错误处理中间件
Caution
错误处理中间件始终接收四个参数。 你必须传入四个参数,才能将其标识为错误处理中间件函数。 即使你不需要使用 next 对象,也必须声明它,以保持函数签名不变。 否则,next 对象会被解析为常规中间件,从而无法处理错误。
定义错误处理中间件函数的方式与其他中间件一致,唯一区别是它接收四个参数而非三个,标准函数签名为:(err, req, res, next)。
app.use((err, req, res, next) => { console.error(err.stack); res.status(500).send('Something broke!');});import { type Request, type Response, type NextFunction } from 'express';
app.use((err: Error, req: Request, res: Response, next: NextFunction) => { console.error(err.stack); res.status(500).send('Something broke!');});For details about error-handling middleware, see: Error handling.
内置中间件
从 4.x 版本开始,Express 不再依赖 Connect。 之前包含在 Express 中的中间件函数,现在已经拆分到独立的模块中;查看中间件函数列表。
Express 包含以下内置中间件函数:
- express.static serves static assets such as HTML files, images, and so on.
- express.json parses incoming requests with JSON payloads. 注意:此功能仅在 Express 4.16.0 及以上版本可用
- express.urlencoded parses incoming requests with URL-encoded payloads. 注意:此功能仅在 Express 4.16.0 及以上版本可用
第三方中间件
使用第三方中间件可以为 Express 应用拓展功能。
先安装实现所需功能的 Node.js 模块,之后在应用级别或路由级别将其引入项目。
下面的示例演示了如何安装并加载 cookie 解析中间件 cookie-parser。
npm install cookie-parseryarn add cookie-parserpnpm add cookie-parserbun add cookie-parserconst express = require('express');const app = express();const cookieParser = require('cookie-parser');
// load the cookie-parsing middlewareapp.use(cookieParser());import express from 'express';import cookieParser from 'cookie-parser';
const app = express();
// load the cookie-parsing middlewareapp.use(cookieParser());import express, { type Express } from 'express';import cookieParser from 'cookie-parser';
const app: Express = express();
// load the cookie-parsing middlewareapp.use(cookieParser());如需查看可配合 Express 使用的常用第三方中间件部分清单,请参阅:第三方中间件。