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
我像这样实现了一个中间件(用于缓存)(从实际示例中简化):
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