# 中间件

Express文档链接

# 概念

如果只用一个大函数来处理请求,随着项目的增大,这个函数会变得越来越难维护。 使用中间件,来把这个大函数来分解成多个小函数,一次只处理一件事。

从本质上来说,一个 Express 应用就是在调用各种中间件。

从概念上讲,中间件是一种功能的封装方式。

在使用上讲,中间件只是一个有 3 个参数的函数: 一个 请求对象、一个 响应对象 和一个 next 函数。(还有一种 4 个参数的形式,用来做错误处理). 当 next 函数被调用, Express 就会执行 中间件组 的下一个函数.

Screen Shot 2018-07-23 at 1.32.46 PM

可以把中间件想象成 水管。水从一端入,在到达出水口的过程中,会经历各种 仪表 和 阀门。而这个过程中最重要的就是顺序。在其中一个阀门中注入一种原料,流过这个阀门之后的水都会含有这种原料。

# 应用级中间件

在 Express 程序中,通过调用 app.use

app.use(function (req, res, next) {
  ... 其他代码 ...
});

由传入每个中间件的 next函数 控制在 “管道” 中的请求是否终止向下传。调用 next() 方法将控制权交给下一个中间件,否则请求就会挂起。

Screen Shot 2018-07-08 at 5.03.52 PM

Screen Shot 2018-07-08 at 5.05.47 PM

在 Node 中,请求对象 和 响应对象 只被传递进一个请求处理函数. 但在 Express 中,请求对象 和 响应对象 被传递到一个 中间件堆栈(middleware stack), 并且从第一个函数开始, 一直被传递到最后一个。 最后一个函数中, 必须调用 res.end() 以来结束此次请求.

# 错误处理中间件

误处理中间件和其他中间件定义类似,只是要使用 4 个参数,而不是 3 个 (err, req, res, next)。

app.use(function(err, req, res, next) {
  console.log(err);
});

在应用中, 一般在其他 app.use() 和路由调用后,最后定义错误处理中间件, 也就是说错误处理是放在最后的中间件.

Express 内置了一个错误处理句柄,它可以捕获应用中可能出现的任意错误。这个缺省的错误处理中间件将被添加到中间件堆栈的底部。

如果你向 next() 传递了一个 error ,而你并没有在错误处理句柄中处理这个 error,Express 内置的缺省错误处理句柄就是最后兜底的。最后错误将被连同堆栈追踪信息一同反馈到客户端。

堆栈追踪信息并不会在生产环境中反馈到客户端。

设置环境变量 NODE_ENV 为 “production” 就可以让应用运行在生产环境模式下。

# 中间件 和 路由处理器

路由处理器(app.getapp.post,被统称为 app.VERB)可以被看作只处理特定 HTTP 请求的中间件。 可以将 app.use 看作可以处理全部 HTTP 请求的路由处理器。(基本上等同于 app.all

app.get('/', function (req, res) {
  res.send('GET request to the homepage');
});

路由处理器的第一个参数必须是路径。

// 没有挂载路径的中间件,应用的每个请求都会执行该中间件
app.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});

// 挂载至 /user/:id 的中间件,任何指向 /user/:id 的请求都会执行它
app.use('/user/:id', function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

中间件也可以将路径作为第一个参数,但它是可选的(如果忽略这个参数,它会匹配所 有路径)。 路由处理器和中间件的参数中都有回调函数,这个函数有 2 个、3 个或 4 个参数。

  • 如果有 2 个或 3 个参数,头两 个参数是 请求 和 响应对象,第三个参数是 next 函数。

  • 如果有 4 个参数,它就变成了 错误处理中间件,第一个参数变成了 error 对象,然后依次是 请求、响应 和 next 对象。

如果不调用 next(),请求会被挂起,也不会再有处理器或中间件做后续处理。 如果你不调用 next(),则应该发送一个响应到客户端(res.sendres.jsonres.render 等) 如果你不这样做,请求会被挂起并最终导致超时。

如果调用了 next(),一般就不要发送响应到客户端。如果你发送了,管道中后续的 中间件 或 路由处理器 还会执行,但它们发送的任何响应都会被忽略。

上次更新: 7/4/2020, 4:14:54 AM