express js中间件res.locals after next

Express js middleware res.locals after next

我像这样实现了一个中间件(用于缓存)(从实际示例中简化):

import asyncHandler from 'express-async-handler';
import cache from '../cache';

export async function cacheMiddleware(req, res, next) {

  // Check if present in cache
  res.locals.fromCache = cache.get(res.locals.params.cacheKey);

  // Call subsequent middleware, but DO NOT return
  next();

  // After subsequent middleware chain, Update the cache if needed
  if (res.locals.accessGranted === true) {
    cache.set(res.locals.params.cacheKey, true);
  }

}

export default asyncHandler(cacheMiddleware);

在随后的中间件中,我使用 res.locals.fromCache 来确定请求是否具有缓存值。效果很好。

如您所见,我还想在同一个中间件中处理添加到缓存的过程。通过在 next 之后不返回,在中间件链完成后,调用堆栈 returns 到此中间件以执行一些额外的操作。

让我吃惊的是,我期望 res.locals 成为一个对象引用,因此在后续中间件中所做的任何修改现在都可以在这里获得。如您所见,在这种情况下我试图访问res.locals.accessGranted(在后续中间件中设置)来确定我是否需要更新缓存,但是属性上不存在res.locals 在此范围内。这让我很惊讶,我猜 res 一定是深拷贝!?

是否有执行此类操作的最佳实践,或者是否有一些理由根本不执行此操作?这似乎是一个非常简洁的解决方案(以及我在 Python 的类似框架中成功使用的模式)并且比使用 2 个中间件来获取和设置缓存更可取。

好问题!我个人认为这种方法可能会让您有些纠结,首先我会解释您遇到的问题,然后我会解释为什么我认为您应该稍微改变一下方法。

您遇到的问题是竞争条件。在此特定实例中,next() 是非阻塞的。它说 "okay I'm done, Express please continue on" 并且 express 继续它的快乐方式。如果您在程序中添加一些控制台日志,您会注意到调用 next() 之后的行可能在您的后续处理程序有时间完成之前执行,特别是如果它们正在对数据库进行异步调用什么的。

实际上你是 运行 你的 if 语句并且 cache.set 在下一个中间件完成之前,从而导致竞争条件。

我不相信,虽然我最近没有检查过,next()会让你传递和接收回调,这意味着你是最好的将这个中间件一分为二。我会让缓存检索作为您的第一个函数发生,然后调用您的其他中间件函数,最后调用一个执行任何缓存更新的函数。把中间件想象成不像一个模块,而更像是一个普通的旧函数,这比拥有一个重载的中间件函数更有意义。

所以现在你会

fetchFromCache -> [various middleware, perhaps some hefty controllers] -> updateCache