node学习笔记 express中间件

中间件

中间件(Middleware) 是一个函数,它可以访问请求对象(request object (req)), 响应对象(response object (res)), 和 web 应用中处于请求-响应循环流程中的中间件,一般被命名为 next 的变量。
中间件的功能包括:

  • 执行任何代码。
  • 修改请求和响应对象。
  • 终结请求-响应循环。
  • 调用堆栈中的下一个中间件。
    如果当前中间件没有终结请求-响应循环,则必须调用 next() 方法将控制权交给下一个中间件,否则请求就会挂起

    应用级中间件

    应用级中间件绑定到 app 对象 使用 app.use() 和 app.METHOD(), 其中, METHOD 是需要处理的 HTTP 请求的方法,例如 GET, PUT, POST 等等,全部小写。

    1
    var app = express();
  • 路由处理程序
    您可以提供多个回调函数,以类似于中间件的行为方式来处理请求。但这些回调函数可能调用 next(‘route’) 来绕过剩余的路由回调。
    路由处理程序的形式可以是一个函数、一组函数或者两者的结合,如以下示例中所示。

  • 单个

    1
    2
    3
    app.get('/example/a', function (req, res) {
    res.send('Hello from A!');
    });
  • 多个

    1
    app.get('/example/a', (req, res, next)=>{},(req, res)=>{});
  • 混合

    1
    2
    3
    app.get('/example/c', [cb0, cb1, cb2]); //cb0, cb1, cb2均为函数
    app.get('/example/c', [cb0, cb1],(req, res) =>{}); //cb0, cb1均为函数
  • app.route()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    app.route('/book')
    .get(function(req, res) {
    res.send('Get a random book');
    })
    .post(function(req, res) {
    res.send('Add a book');
    })
    .put(function(req, res) {
    res.send('Update the book');
    });

路由级中间件

它使用 express.Router() 产生的实例对象。
将路由器创建为模块,在其中装入中间件(router.METHOD()),然后安装在主应用程序的路径中

在应用程序目录中创建名为 birds.js 的路由器文件,其中包含以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var express = require('express');
var router = express.Router();
// middleware that is specific to this router
router.use(function timeLog(req, res, next) {
console.log('Time: ', Date.now());
next();
});
// define the home page route
router.get('/', function(req, res) {
res.send('Birds home page');
});
// define the about route
router.get('/about', function(req, res) {
res.send('About birds');
});
module.exports = router;

接着,在应用程序中装入路由器模块:

1
2
3
var birds = require('./birds');
//...
app.use('/birds', birds);

此应用程序现在可处理针对 /birds 和 /birds/about 的请求,调用特定于此路由的 timeLog 中间件函数。

区别

“路由级别的中间件,可以作为app级别的中间件的扩展,从而减小app级别路径处理的臃肿,提高可维护性和扩展性”。

实现原理

express内部维护一个函数数组,这个函数数组表示在发出响应之前要执行的所有函数,也就是中间件数组

使用app.use(fn)后,传进来的fn就会被扔到这个数组里,执行完毕后调用next()方法执行函数数组里的下一个函数,如果没有调用next()的话,就不会调用下一个函数了,也就是说调用就会被终止

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
27
28
var express = require('express');
var app = express();
app.listen(3000, function () {
console.log('listen 3000...');
});
function middlewareA(req, res, next) {
console.log('middlewareA before next()');
next();
console.log('middlewareA after next()');
}
function middlewareB(req, res, next) {
console.log('middlewareB before next()');
next();
console.log('middlewareB after next()');
}
function middlewareC(req, res, next) {
console.log('middlewareC before next()');
next();
console.log('middlewareC after next()');
}
app.use(middlewareA);
app.use(middlewareB);
app.use(middlewareC);

运行结果如下

1
2
3
4
5
6
7
listen 3000...
middlewareA before next()
middlewareB before next()
middlewareC before next()
middlewareC after next()
middlewareB after next()
middlewareA after next()

从上述结果可以看到在执行完下一个函数后又会回到之前的函数执行next()之后的部分,即当我们一直调用next()的时候,程序会一直处于递归状态,直到 return 或者执行到函数末尾,然后逐一跳出每层递归函数。

模拟实现

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
27
28
function express() {
var funcs = []; // 待执行的函数数组
var app = function (req, res) {
var i = 0;
function next() {
var task = funcs[i++]; // 取出函数数组里的下一个函数
if (!task) { // 如果函数不存在,return
return;
}
task(req, res, next); // 否则,执行下一个函数
}
next();
}
/**
* use方法就是把函数添加到函数数组中
* @param task
*/
app.use = function (task) {
funcs.push(task);
}
return app; // 返回实例
}