Express中间件SQL取数传递至EJS公共模板渲染为空如何解决
问题背景
正在搭建内容管理系统,期望支持系统管理员修改所有HTML页面元数据等全局系统属性,当前遇到问题:传递给EJS模板的对象数据无法正常渲染,最终输出结果为空。

现有代码与运行状态
Settings Controller(模块运行正常)
// Get all data from the settings to display it on evre page. exports.data = (req, res, next) => { db.query('SELECT * FROM settings', async (error, results, fields) => { if (error) { console.log(error) return next() } else { const settings = results[0] console.log(settings) next() }; }) }
settings.js 路由(模块运行正常)
const express = require("express"); const settingsController = require("../controller/settings") const router = express.Router(); // get all data from database router.get('/settings', settingsController.data ); module.exports = router;
模板结构说明
EJS页面通过include语法引入公共组件,需要修改的是公共HTML头部内容。
index 首页模板(运行正常)
<%- include('./partials/head'); %> </head> <body> <%- include('./partials/navbar'); %> <%- include('./partials/footer'); %>
page.js 路由(运行正常)
// use res.render to load up an ejs view file // index page // 已挂载settings Middleware router.get('/', authController.isLoggedIn,settingsController.data,indexController.home);
异常位置
head.ejs 公共头部模板(模板变量渲染为空)
<!DOCTYPE html> <html lang="en"> <head> <!-- 此处渲染为空 --> <meta name="description" content="<%= settings.description %>">
客户端侧渲染后的首页源码(meta标签content属性为空)
<!DOCTYPE html> <html lang="en"> <head> <!-- 内容为空 --> <meta name="description" content="">
问题原因与解决方案
核心原因
settingsController.data中间件查询到settings数据后,仅将数据存在了中间件函数的局部作用域中,既没有挂载到请求/响应对象传递给后续中间件,也没有在调用res.render时传入模板,EJS无法读取到settings变量,最终渲染结果为空。
修复步骤
- 修改settings中间件,把查询到的配置挂载到
res.locals上。Express的res.locals对象专门用于存储需要传递给模板的变量,挂载后所有后续渲染流程、include引入的子模板都可以直接访问对应变量,不需要逐层手动传参。
修改后的Settings Controller代码:// Get all data from the settings to display it on every page. exports.data = (req, res, next) => { db.query('SELECT * FROM settings', async (error, results, fields) => { if (error) { console.log(error) // 查询异常时设置默认值,避免模板渲染报错 res.locals.settings = { description: '' } return next() } else { // 将查询到的配置挂载到res.locals,模板可直接访问 res.locals.settings = results[0] next() }; }) } - 确认
indexController.home中调用res.render时,不要覆盖res.locals内的settings字段即可。res.render传入的参数对象会和res.locals自动合并,挂载到res.locals的全局变量会自动注入所有模板,包括include引入的head、navbar、footer这类公共组件,不需要额外处理。
额外优化建议
- 全局配置属于所有页面都需要的公共数据,不需要在每个路由里单独挂载
settingsController.data中间件,直接在业务路由挂载前注册全局中间件即可,一次配置所有路由自动生效:// 注册全局中间件,所有路由渲染前自动拉取系统配置 app.use(settingsController.data) - 建议给系统配置增加本地缓存,避免每次页面请求都查询数据库,降低数据库压力。
内容的提问来源于stack exchange,提问作者Nick390
相关产品推荐
相关产品推荐

