前言
最近的学习当中,之前用Express框架写服务端的时候也没注意中间件啥的!直到面试官说你给我讲下中间件的时候,一脸懵逼的我才反应过来原来我都还没去好好研究过这个东西!这次就顺着官方框架说明,边敲边记录,总结一下Express框架的中间吧~~
Express[1] 是一个路由和中间件 Web 框架,其自身只具有最低程度的功能:Express 应用程序基本上是一系列中间件函数调用。
中间件函数能够访问请求对象[2] (req
)、响应对象[3] (res
) 以及应用程序的请求/响应循环中的下一个中间件函数。下一个中间件函数通常由名为 next
的变量来表示。
示例:
// 通用中间件
function middleware(req, res, next) {
// 中间处理逻辑 .....
next();
}
// req为请求 res为响应 next为调用下一个中间件函数
中间件函数可以执行以下任务:
- 执行任何代码。
- 对请求和响应对象进行更改。
- 结束请求/响应循环。
- 调用堆栈中的下一个中间件函数。
如果在当前中间件执行完毕后,没有结束上面的请求/响应循环,那么中间件必须调用next()函数将该请求的控制权交给下一个中间件。否则该请求将保持挂起状态。
Express框架当中根据中间件使用方式的不同可以分为以下几类:
- 应用层中间件
- 路由器层中间件
- 错误处理中间件
- 内置中间件
- 第三方中间件
一、应用层中间件
中间件可以使用app.use()
和 app.METHOD()
函数绑定到应用程序对象的实例。其中,中间件可以在方法的内部定义,也可以在外部定义,在上面两种方法内只是引用;METHOD指代的是请求当中小写的HTTP方法。
中间件函数,后续示例基于以下实例:
const express = require('express');
const app = express();
function middleware(req, res, next) {
console.log('请求来了...');
next();
}
1. app.use()方法绑定
没有安装路径的中间件函数。应用程序每次收到请求时执行该函数。
app.use(middleware);
安装在 /user/:id
路径中的中间件函数。在 /user/:id
路径中为任何类型的 HTTP 请求执行此函数。
app.use('/user/:id', middleware)
上述的中间件写法属于在外部定义,当然也可以在内部如下定义:
app.use('/user/:id', (req, res, next) => {
console.log(req.params.id);
res.send(req.params.id)
})
2. app.method()方法绑定
这种方法是我们常用的方法,一般我们都会用这种方法对特定请求进行处理。
app.get('/user/:id', (req, res, next) => {
console.log(req.params.id);
res.send(req.params.id)
})
3. 小结
多中间件
以下是在安装点使用安装路径装入一系列中间件函数的示例。只要其中一个结束了请求/响应循环,则中间件子堆栈的调用停止。
app.use('/user/:id', middleware, middleware2 ...)
跳跃调用
可以调用 next('route')
将控制权传递给下一个路由。适用于要跳过路由器中间件堆栈中剩余的中间件函数的场景。
注:next('route')
仅在使用 app.METHOD()
或 router.METHOD()
函数装入的中间件函数中有效。
二、路由器层中间件
路由器层中间件的工作方式与应用层中间件基本相同,差异之处在于它绑定到 express.Router()
的实例。
var app = express();
var router = express.Router();
// 路由中间件处理逻辑...
// 挂在路由中间件到应用程序实例
app.use('/', router);
使用 router.use()
和 router.METHOD()
函数装入路由器层中间件。和上述应用层中间件用法大致相似:
没有安装路径的中间件,为每个请求执行此中间件:
router.use(function (req, res, next) {
console.log('Time:', Date.now());
next();
});
定义了路径,并调用中间件子堆栈处理请求:
router.get('/user/:id', function (req, res, next) {
// 如果用户ID为0 则跳到下一个路由
if (req.params.id == 0) next('route');
// 否则将控制权交给该子堆栈中的下一个中间件处理
else next();
}, function (req, res, next) {
// 渲染页面
res.render('regular');
});
三、错误处理中间件
错误处理与其他中间件函数最大的差别在于有四个自变量 (err, req, res, next)
:
app.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something broke!');
});
四、内置中间件
Express当中唯一的内置中间件函数是 express.static
,负责提供 Express 应用程序的静态资源。
app.use(express.static(rootDir, options));
其中rootDir
是提供静态资源的根目录,options
是可选的对象,可以具有以下属性:
参考代码:
app.use(express.static('public', {
dotfiles: 'ignore',
etag: false,
extensions: ['htm', 'html'],
index: false,
maxAge: '1d',
redirect: false,
setHeaders: function (res, path, stat) {
res.set('x-timestamp', Date.now());
}));
五、第三方中间件
除了自定义的中间件和内置中间件,在开发过程当中肯定少不了的会用到第三方的一些中间件来向 Express 应用程序添加功能。
常用的第三方插件包括 body-parser, compression, cookie-parser, cookie-session
等。
使用第三方中间件时,需安装具有所需功能的 Node.js 模块,然后在应用层或路由器层的应用程序中将其加装入。
安装 cookie-parser
中间件示例:
$ npm install cookie-parser
var express = require('express');
var app = express();
var cookieParser = require('cookie-parser');
// load the cookie-parsing middleware
app.use(cookieParser());
小结
中间件的本质是一个函数,在服务端处理请求的时,在收到请求和返回响应的过程中为我们做一些工作。每一个中间件交个下一个中间件的是对请求的控制权。
参考资料
[1]Express: https://expressjs.com/zh-cn/
[2]请求对象: https://expressjs.com/zh-cn/4x/api.html#req
[3]响应对象: https://expressjs.com/zh-cn/4x/api.html#res
本文首发于公众号:达哥的号