
本文介绍一种轻量、可复用的 Express 中间件方案,通过解析 req.headers.host 实现多域名共享同一端口、按需挂载不同静态资源或 Router 的条件路由,适用于本地开发多站管理及生产环境多租户静态/动态站点托管。
本文介绍一种轻量、可复用的 express 中间件方案,通过解析 `req.headers.host` 实现多域名共享同一端口、按需挂载不同静态资源或 router 的条件路由,适用于本地开发多站管理及生产环境多租户静态/动态站点托管。
在 Express 应用中,若需根据访问域名(如 site-a.com、site-b.local)动态加载对应站点的路由逻辑或静态资源,不能在 app.get('/') 回调内调用 app.use() —— 因为 Express 的中间件挂载必须在应用启动阶段完成,运行时调用将无效且可能引发不可预期行为。
正确的做法是:封装一个高阶中间件(Higher-Order Middleware),接收域名白名单与目标处理器(Router 或静态中间件),在请求进入时实时匹配并委托处理。该方案解耦了路由判定与业务逻辑,支持任意数量的站点配置,且完全兼容 Express 的中间件生命周期。
✅ 推荐实现:域名感知路由中间件
创建 middleware/domainRouter.js:
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
// middleware/domainRouter.js
module.exports = (hosts, handler) => {
return (req, res, next) => {
const host = req.headers.host?.split(':')[0] || ''; // 忽略端口(如 localhost:3000 → localhost)
if (hosts.includes(host)) {
// 直接调用 Router 实例或中间件函数
if (typeof handler === 'function') {
handler(req, res, next);
} else {
// 若 handler 是 Express Router(推荐),直接 use 即可
// 注意:此处需确保 handler 已正确初始化(如 require('./routes') 返回 router)
handler(req, res, next);
}
} else {
next(); // 不匹配则继续后续中间件
}
};
};
⚠️ 注意:req.hostname 在某些反向代理(如 Nginx)下可能不可靠,建议统一使用 req.headers.host 并手动剥离端口以提高兼容性。
? 使用方式:按域名挂载不同 Router 或静态服务
在主应用入口(如 app.js)中:
const express = require('express');
const app = express();
const domainRouter = require('./middleware/domainRouter');
// ✅ 站点 A:使用自定义 Router(如 pages/site-A/routes.js 导出的 express.Router)
const siteARouter = require('./pages/site-A/routes');
app.use(domainRouter(['localhost', 'site-a.local', 'site-a.com'], siteARouter));
// ✅ 站点 B:直接托管静态文件(注意:需配合 sendFile 或 static + index fallback)
const siteBStatic = express.static(path.join(__dirname, 'pages/site-B-html'));
app.use(domainRouter(['site-b.local', 'site-b.com'], siteBStatic));
// 同时需补充根路径的 index.html 响应(否则 / 访问 404)
app.get('/', (req, res, next) => {
if (['site-b.local', 'site-b.com'].includes(req.headers.host?.split(':')[0])) {
res.sendFile(path.join(__dirname, 'pages/site-B-html', 'index.html'));
} else {
next();
}
});
// ✅ 站点 C:混合使用(Router + 静态资源)
const siteCRouter = require('./pages/site-C/routes');
app.use(domainRouter(['site-c.local', 'site-c.com'], siteCRouter));
// ? 默认兜底:所有未匹配域名走 default 站点
app.use(express.static(path.join(__dirname, 'pages/default')));
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'pages/default', 'index.html'));
});
? 关键优势与注意事项
- 零侵入式架构:无需修改原有 Router 代码,只需按需组合中间件;
- 开发友好:配合 /etc/hosts 映射多个 .local 域名(如 127.0.0.1 site-a.local),实现本地多站并行调试;
- 生产就绪:支持真实域名(如 example.com、www.example.com),可扩展支持子域名(如 admin.example.com);
- 性能安全:域名匹配为 O(1) 数组查找,无正则开销;中间件仅在匹配时执行,其余请求快速跳过;
- 错误规避:避免在 app.get() 内调用 app.use();禁止在响应已发送后调用 next();静态托管务必处理 GET / 路由以返回 index.html。
通过这一模式,你既能保持 Express 的简洁性,又能灵活支撑多站点、多租户、甚至灰度发布等进阶场景——真正实现“一套服务,多域分发”。










