express中间件原理connect

本文涉及的产品
性能测试 PTS,5000VUM额度
云原生网关 MSE Higress,422元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 不知道用了express.js的你有没有这样的疑问:

image.png


  • app.use为什么可以添加一个又一个中间件?
  • connect是如何区分普通中间件和错误中间件的?
  • 中间件处理函数中的next指代的又是什么?

我简单看了一下connect源码,弄清楚了上面的这3个问题。


app.use为什么可以添加一个又一个中间件?


app.use(function middleware1(req, res, next) {
  // middleware 1
  next();
});
app.use(function middleware2(req, res, next) {
  // middleware 2
  next();
});


connect维护了一个中间件栈(middleware stack)

数据结构:栈(stack)


每次调用use,都会向这个应用(app)实例的栈(stack)推入一个带路径和处理函数的对象。


源码:

function createServer() {
  function app(req, res, next){ app.handle(req, res, next); }
  // ...
  app.stack = []; // 注意这里
  return app;
}
proto.use = function use(route, fn) {
  var handle = fn;
  var path = route;
  // ...
  // add the middleware
  this.stack.push({ route: path, handle: handle });
  return this;
};


connect是如何区分普通中间件和错误中间件的?


// regular middleware
app.use(function (req, res, next) {
  next(new Error('boom!'));
});
// error middleware
app.use(function onerror(err, req, res, next) {
  // an error occurred!
});


JavaScript的函数的长度属性:length。


这么说可能比较绕,看下面这个例子就懂了。


例如


function test1(foo,bar){ }
test.length // 2
function test2(foo,bar,baz){ }
test.length // 3


function test1(foo,bar){ }
test.length // 2
function test2(foo,bar,baz){ }
test.length // 3


connect正是通过中间件处理函数的形参长度来区分出普通中间件和错误中间件的。


function call(handle, route, err, req, res, next) {
  var arity = handle.length;
  var error = err;
  var hasError = Boolean(err);
  try {
    if (hasError && arity === 4) {
      // error-handling middleware
      handle(err, req, res, next);
      return;
    } else if (!hasError && arity < 4) {
      // request-handling middleware
      handle(req, res, next);
      return;
    }
  } catch (e) {
    // replace the error
    error = e;
  }
  // continue
  next(error);
}


看了源码,官方文档对错误处理中间件描述skipping any error middleware above that middleware and any non-error middleware below的解释其实也懂了:

  • 跳过前面的所有错误中间件:index值是递增的,请求只走后面的错误中间件
  • 跳过后面的非异常处理中间件:异常中间件两个条件都满足请求会进入,非异常中间件由于hasError为true因此请求不会进入

只能有一个异常处理中间件吗?


可以有多个。(官方文档+亲测)

app.use(logErrors)
app.use(clientErrorHandler)
app.use(errorHandler)
// error middleware one
app.use(function onerror(err, req, res, next) {
  // an error occurred!
  next(err) // 注意要这么写,next()进入不到下一个异常处理中间件
});
// error middleware two
app.use(function onerror(err, req, res, next) {
  // an error occurred!
});


中间件处理函数中的next指代的又是什么?


指代的是栈中的下一个中间件。

proto.handle = function handle(req, res, out) {
  var index = 0;
  var stack = this.stack;
  // ...
  function next(err) {
    // next callback
    var layer = stack[index++];
    // call the layer handle
    call(layer.handle, route, err, req, res, next);
  }
  next();
};

通过上面代码可以看出,每次调用next()函数,会执行index++,layer为middleware stack中的下一个中间件。


其中layer.handle来自于this.stack.push({ route: path, handle: handle });。



相关文章
|
JSON 前端开发 中间件
axios基本使用,express中间件
axios基本使用,express中间件
|
JSON JavaScript 中间件
node.js中Express框架路由,中间件
node.js中Express框架路由,中间件
|
3月前
|
中间件 API 开发者
深入理解Python Web框架:中间件的工作原理与应用策略
在Python Web开发中,中间件位于请求处理的关键位置,提供强大的扩展能力。本文通过问答形式,探讨中间件的工作原理、应用场景及实践策略,并以Flask和Django为例展示具体实现。中间件可以在请求到达视图前或响应返回后执行代码,实现日志记录、权限验证等功能。Flask通过装饰器模拟中间件行为,而Django则提供官方中间件系统,允许在不同阶段扩展功能。合理制定中间件策略能显著提升应用的灵活性和可扩展性。
42 4
|
7月前
|
存储 缓存 JSON
玩转Express(二)登录态&中间件
玩转Express(二)登录态&中间件
|
5月前
|
消息中间件 缓存 IDE
MetaQ/RocketMQ 原理问题之消息队列中间件的问题如何解决
MetaQ/RocketMQ 原理问题之消息队列中间件的问题如何解决
|
5月前
|
中间件 API 开发者
深入理解Python Web框架:中间件的工作原理与应用策略
【7月更文挑战第19天】Python Web中间件摘要:**中间件是扩展框架功能的关键组件,它拦截并处理请求与响应。在Flask中,通过`before_request`和`after_request`装饰器模拟中间件行为;Django则有官方中间件系统,需实现如`process_request`和`process_response`等方法。中间件用于日志、验证等场景,但应考虑性能、执行顺序、错误处理和代码可维护性。
82 0
|
6月前
|
存储 中间件 数据安全/隐私保护
中间件中OAuth 2.0 和 OpenID Connect
【6月更文挑战第4天】
257 5
|
7月前
|
存储 缓存 中间件
中间件Read-Through Cache(直读缓存)策略工作原理
【5月更文挑战第11天】中间件Read-Through Cache(直读缓存)策略工作原理
76 3
|
中间件
93 # 实现 express 错误处理中间件
93 # 实现 express 错误处理中间件
62 0
|
消息中间件 存储 监控
消息中间件第八讲:消息队列 RocketMQ 版实战、集群及原理
消息中间件第八讲:消息队列 RocketMQ 版实战、集群及原理
354 0