一、中间件类型
1、应用程序级中间件
基本用法:挂载于app实例中:
const express = require('express');
let app = express()
app.use(function (req, res, next) {
console.log('现在时间是:', Date.now())
next()})
//没有装载路径的中间件功能,每次应用程序收到请求时都会执行该功能。
app.use('/user/:id', function (req, res, next) {
console.log('Req:', req.method)
next()})
//在 /user/:id 路径上安装的中间件功能。对/user/:id路径上的任何类型的HTTP请求执行该函数。
app.get('/user/:id', function (req, res, next) {
res.send('USER')})
2、路由器级中间件
基本用法:挂载于express.Router()实例中,其他类似于上面
3、错误处理中间件
不同于前面两个,错误处理中间件需要四个参数(err, req, res, next)。必须提供四个参数以将其标识为错误处理中间件函数。即使不需要使用该next对象,也必须指定它以保持签名。该next对象将被解释为常规中间件,并且将无法处理错误。
4、内置中间件
常见的有:
- express.static提供静态资源,如
HTML
文件,图像等。 - express.json使用JSON有效负载解析传入的请求。
- express.urlencoded用URL编码的有效负载解析传入的请求
详见:Express.js express.json() Function - GeeksforGeeks
5、第三方中间件
二、应用程序级和路由级的next函数原理不同
next函数主要负责将控制权交给下一个中间件,如果当前中间件没有终结请求,并且next没有被调用,那么请求将被挂起,后边定义的中间件将得不到被执行的机会。
理论上,所有的中间件在处理控制权交接以及中间件跳转时,都需要调用next。但是,当一个中间件成功处理完请求并返回响应,此时代表中间件终止!不应该再在后面调用next函数,否则会执行没有必要的中间件,导致错误。
例子:
app.get('/a', function(req, res, next) {
res.send('sucess');
next();
});
// catch 404 and forward to error handler
app.use(function(req, res, next) {
console.log(404);
var err = new Error('Not Found');
err.status = 404;
next(err);
});
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
//控制台输出内容
404
GET /a 500 6.837 ms - -
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:345:11)
1、应用程序级next
其next源码如下,可以看出其会遍历堆栈中的中间件,将其做依次匹配,若匹配成功则执行【对于app.use默认匹配路劲是'/',即匹配任何请求】
function next(err) {
... //此处源码省略
// find next matching layer
var layer;
var match;
var route;
while (match !== true && idx < stack.length) {
layer = stack[idx++];
match = matchLayer(layer, path);
route = layer.route;
if (typeof match !== 'boolean') {
// hold on to layerError
layerError = layerError || match;
}
if (match !== true) {
continue;
}
... //此处源码省略
}
... //此处源码省略
// this should be done for the layer
if (err) {
layer.handle_error(err, req, res, next);
} else {
layer.handle_request(req, res, next);
}
}
2、路由级next
其执行原理更为简单,如下:
function next(err) {
if (err && err === 'route') {
return done();
}
var layer = stack[idx++];
if (!layer) {
return done(err);
}
if (layer.method && layer.method !== method) {
return next(err);
}
if (err) {
layer.handle_error(err, req, res, next);
} else {
layer.handle_request(req, res, next);
}
}
它负责同一个路由下多个中间件的控制权的传递,并且它会接收一个参数"route",如果调用next(“route”),则会跳过执行当前的中间件,直接将控制权交给下一个匹配的路由 。
例子:
const express = require('express');
const app = express();
// 中间件1
app.use('/example', (req, res, next) => {
console.log('Middleware 1');
next(); // 继续执行下一个中间件或路由处理函数
});
// 中间件2
app.use('/example', (req, res, next) => {
console.log('Middleware 2');
next('route'); // 跳过当前中间件,继续执行下一个匹配的路由中间件
});
// 路由处理函数
app.get('/example', (req, res) => {
console.log('Route Handler');
res.send('Hello, World!');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
在这个例子中,当访问 /example
路径时,首先会执行中间件1,然后执行中间件2。在中间件2中,调用 next('route')
会跳过当前中间件,并直接执行路由处理函数。因此,在浏览器中访问 /example
路径时,只会输出 "Route Handler",而不会输出 "Middleware 2"。