从ExpressJS的app.locals渲染EJS模板时为何出现“未定义”错误?
ExpressJS中app.locals变量未定义问题的解决
问题描述
在ExpressJS应用中编写了如下请求端点代码:
app.get('/', (req, res, next) => { db.collection('sys_params').find().toArray((err, sysParams) => { if (err) throw err; app.locals.landPageRolloverStatus = sysParams[0].landPageRolloverStatus; app.locals.ludenourOnlineStatus = sysParams[0].ludenourOnlineStatus; app.locals.ludenourContactUsEnabled = sysParams[0].ludenourContactUsEnabled; app.locals.ludenourJobsEnabled = sysParams[0].ludenourJobsEnabled; console.log(sysParams) app.locals.sysParams = sysParams; }); res.render('index2'); next() });
渲染EJS模板index2.ejs时,出现如下错误:
ReferenceError: /Users/falswaimil/Documents/Project3X/Master/AIRECP/AirECPRetails/online-store/views/index2.ejs:39 37| 38| <a class="navbar-brand float-xs-right float-sm-left" href="http://localhost:3002/?clang=en"><img src="img/lu3.svg" class="img-fluid"/></a> >> 39| <% if (sysParams[0].ludenourOnlineStatus == "UC" ) { %> 40| 41| <% if (clang == "en") { %> 42| <p style="color:red; font-family:Arial Rounded MT Bold; font-size:130%; text-align:center; width: 100%;"><%= __('SiteUnderConstruction') %></p> sysParams is not defined
但刷新页面后模板能正常渲染,用户疑惑:app.locals作为应用级变量,应该跨请求在EJS模板中可用,为什么会出现这个问题?
问题原因
核心问题是异步代码执行顺序:
- 数据库查询
db.collection('sys_params').find().toArray()是异步操作,它的回调函数会在查询完成后才执行。 - 当前代码中,
res.render('index2')和next()是在数据库查询发起后立即执行的,此时回调还没跑完,app.locals.sysParams还没被赋值,所以模板渲染时找不到该变量。 - 刷新页面时,上一次请求的异步回调已经完成,
app.locals.sysParams已经被赋值,所以模板能正常读取。
解决方法
需要确保在数据库查询完成、app.locals赋值完成后,再执行模板渲染。有两种常用方式:
方式1:将res.render放入异步回调内
app.get('/', (req, res, next) => { db.collection('sys_params').find().toArray((err, sysParams) => { if (err) { // 错误处理,避免直接throw导致应用崩溃 return next(err); } app.locals.landPageRolloverStatus = sysParams[0].landPageRolloverStatus; app.locals.ludenourOnlineStatus = sysParams[0].ludenourOnlineStatus; app.locals.ludenourContactUsEnabled = sysParams[0].ludenourContactUsEnabled; app.locals.ludenourJobsEnabled = sysParams[0].ludenourJobsEnabled; app.locals.sysParams = sysParams; // 赋值完成后再渲染模板 res.render('index2'); next(); }); });
方式2:使用async/await简化异步代码
app.get('/', async (req, res, next) => { try { const sysParams = await db.collection('sys_params').find().toArray(); app.locals.landPageRolloverStatus = sysParams[0].landPageRolloverStatus; app.locals.ludenourOnlineStatus = sysParams[0].ludenourOnlineStatus; app.locals.ludenourContactUsEnabled = sysParams[0].ludenourContactUsEnabled; app.locals.ludenourJobsEnabled = sysParams[0].ludenourJobsEnabled; app.locals.sysParams = sysParams; res.render('index2'); next(); } catch (err) { next(err); } });
额外说明
app.locals确实是应用级变量,一旦赋值后会在后续所有请求中生效,但第一次请求时必须确保赋值完成后再渲染模板。- 建议不要直接在回调中
throw err,应该通过next(err)把错误交给Express的错误处理中间件,避免应用崩溃。
内容的提问来源于stack exchange,提问作者فوزي الصويمل Fawzi Alswaimil
相关产品推荐
相关产品推荐

