如何基于域名在Express Router中条件加载路由实现多站点部署?
问题
我知道很多人会建议我"重新考虑应用架构",但请不要这么做。该方案契合我的实际需求,希望能得到实用的解决方案。
我已有代码可根据域名提供不同静态文件,实现同路由下多静态HTML站点的运行:
app.get('/', (req, res) => { console.log('req.hostname: ', req.hostname) if (req.hostname === 'localhost') { app.use(express.static(path.join(__dirname, 'pages/site-A/'))); res.sendFile(path.join(__dirname, 'pages/site-A/index.html')); } else { app.use(express.static(path.join(__dirname, 'pages/default/'))); res.sendFile(path.join(__dirname, 'pages/default/index.html')); } });
这段代码运行正常。但我正在开发的用于管理这些站点的主站点更为复杂,它拥有独立的路由。通常我会通过以下方式引入该路由:
app.use('/', require('pages/site/routes'));
请问如何基于域名在'/'路由中条件性地使用我的路由文件?
app.get('/', (req, res) => { console.log('req.hostname: ', req.hostname) if (req.hostname === 'localhost') { app.use(express.static(path.join(__dirname, 'pages/site-A/'))); res.sendFile(path.join(__dirname, 'pages/site-A/index.html')); } else if (req.hostname === 'other.local') { // equivalent of res.send but for app.use('/', require('pages/site/routes')); } else { app.use(express.static(path.join(__dirname, 'pages/default/'))); res.sendFile(path.join(__dirname, 'pages/default/index.html')); } });
解决方案
不能在app.get('/')的回调内调用app.use——app.use是用来注册全局/路由级中间件的,会在应用启动阶段生效,而非每次请求时动态注册。下面是两种实用的实现方式:
方法一:域名过滤中间件+路由挂载
提前引入管理站点的路由模块,通过中间件判断域名,仅匹配other.local时才走该路由:
// 提前引入路由模块,避免重复加载 const adminRoutes = require('pages/site/routes'); // 域名判断中间件 const checkAdminDomain = (req, res, next) => { if (req.hostname === 'other.local') { // 域名匹配,继续执行管理路由 return next(); } // 域名不匹配,跳过当前路由栈,去匹配下一个同路径的处理逻辑 next('route'); }; // 挂载带域名校验的管理路由 app.use('/', checkAdminDomain, adminRoutes); // 处理静态站点的根路由逻辑 app.get('/', (req, res) => { console.log('req.hostname: ', req.hostname); let staticPath, indexPath; if (req.hostname === 'localhost') { staticPath = path.join(__dirname, 'pages/site-A/'); indexPath = path.join(staticPath, 'index.html'); } else { staticPath = path.join(__dirname, 'pages/default/'); indexPath = path.join(staticPath, 'index.html'); } // 注意:不要在请求回调里重复注册static中间件,建议提前注册 app.use(express.static(staticPath)); res.sendFile(indexPath); });
next('route')是关键:它会让Express跳过当前路由链中剩余的中间件,直接匹配下一个同路径的路由规则(也就是后面的app.get('/'))。
方法二:分域名隔离所有路由逻辑
如果想让代码结构更清晰,可以把每个域名对应的处理逻辑都用中间件隔离:
const adminRoutes = require('pages/site/routes'); // 处理localhost域名的静态站点 app.use('/', (req, res, next) => { if (req.hostname !== 'localhost') return next('route'); const staticPath = path.join(__dirname, 'pages/site-A/'); app.use(express.static(staticPath)); res.sendFile(path.join(staticPath, 'index.html')); }); // 处理other.local域名的管理路由 app.use('/', (req, res, next) => { if (req.hostname !== 'other.local') return next('route'); next(); }, adminRoutes); // 处理默认域名的静态站点 app.use('/', (req, res) => { const staticPath = path.join(__dirname, 'pages/default/'); app.use(express.static(staticPath)); res.sendFile(path.join(staticPath, 'index.html')); });
重要优化提示
你原代码存在一个隐患:每次请求/时都调用app.use(express.static(...)),这会重复注册静态文件中间件,长期运行会导致内存泄漏。建议提前一次性注册所有静态目录:
// 提前注册所有静态目录,避免重复注册 app.use(express.static(path.join(__dirname, 'pages/site-A/'))); app.use(express.static(path.join(__dirname, 'pages/default/'))); // 根路由仅负责根据域名返回对应index.html app.get('/', (req, res) => { let indexPath; if (req.hostname === 'localhost') { indexPath = path.join(__dirname, 'pages/site-A/index.html'); } else { indexPath = path.join(__dirname, 'pages/default/index.html'); } res.sendFile(indexPath); });
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

