我正在尝试让我的应用基于从数据库加载的数据(mongodb + mongoose)创建路由;
这是我目前拥有的:
var app = express();
var articleRoute = require('./article.js');
var Articles = require('./models/articles.js');
Articles.get((err, articles) => {
if (err) return console.log('error');
//loop through all articles
articles.forEach(article=> {
app.use('/'+article.slug, articleRoute);
});
});
module.exports = app;
文章已成功获取,但不会作为模块包含在应用中.exports是在从数据库获取文章之前设置的。
如果我改为将该行放在回调中,如下所示:
var app = express();
var articleRoute = require('./article.js');
var Articles = require('./models/articles.js');
Articles.get((err, articles) => {
if (err) return console.log('error');
//loop through all articles
articles.forEach(article=> {
app.use('/'+article.slug, articleRoute);
});
module.exports = app;
});
然后,该应用程序实际上似乎已经退出,没有设置任何导出,并且我得到了此错误消息:TypeError: app.set is not a function
,该错误消息是从我的主脚本中这样调用的:
var app = require('../app');
app.set('port', 3000);
答案 0 :(得分:1)
首先,我强烈建议您在顶层路线与条塞相匹配的设计中使用。这样就不会给您提供唯一的名称空间,该名称空间不会与文章标签冲突,从而为您的网站页面,Ajax路由等创建正常路径。在构建和维护网站以放置文章的所有动态路由时,通常这样会更清洁某些静态路径下的子弹,例如:
/article
然后,文章条目与运行您的网站的其余路径之间不会有任何冲突。我还要指出,它更像是“ REST风格”,因为您在路径顶部指定了所请求的资源类型,然后将资源名称添加为第二级路径。
如何在设置module.exports之前等待异步功能完成
在当前版本的node.js中,您不能等待异步操作完成后再设置导出。不能做有人提议在将来的node.js版本中允许使用该版本,但现在不在这里。
导出和导入是同步的。因此,异步获取的值尚不能用于导出或导入。
通常的解决方法是,导出一个可以调用的方法,该方法可以返回一个promise或接受一个回调,然后在该方法中获取异步值,然后调用方使用返回的promise或传入回调和异步值通过promise或回调传递回去。
第一版可以工作
仅供参考,如果您不添加优先于异步操作完成时添加的路由的通用路由,则您的第一个版本仍然可以使用。在异步代码完成其工作之前,这些路由仅在app
对象上不可用。可以导出一个app
对象,该对象在导出后将添加一些路由。该构造有效,尽管您的服务器可以在没有附加所有路由的情况下运行,并且在articles.forEach()
完成后,将添加其余路由(我认为这花费不到几秒钟,因此可能不是很大)。
第二个版本不起作用
我想您知道,您的第二个版本根本不起作用,因为异步分配module.exports不起作用。呼叫者将已经抓住了空的module.exports
对象,以后再分配时将看不到您的app
。
导出主要启动文件调用的异步方法
解决此问题的经典方法是导出添加路线的方法:
const app = express();
const articleRoute = require('./article.js');
const Articles = require('./models/articles.js');
const promisify = require('util').promisify;
const Articles.getPromise = promisify(Articles.get);
function assignArticleRoutes() {
return Articles.getPromise().then(articles => {
//loop through all articles
articles.forEach(article=> {
app.use('/'+article.slug, articleRoute);
});
}).catch(err => {
console.log("Error loading article routes", err);
throw err;
});
}
module.exports = {app, assignArticleRoutes};
然后,从主启动文件中调用该方法:
const {app, assignArticleRoutes} = require('../app');
app.set('port', 3000);
assignArticleRoutes().then(() =>> {
// start server only after all routes have been added
app.listen(app.get('port'));
}).catch(err => {
// decide what to do here if article routes can't be loaded
});
对所有文章使用单一动态路由
让问题更具灵活性的另一种思考方式是在路线上使用静态商品前缀,以对其进行预先配置,然后根据需要加载商品。之所以可行,是因为您已经对所有商品路线使用了通用的请求处理程序:
const app = express();
const articleRoute = require('./article.js');
const Articles = require('./models/articles.js');
const promisify = require('util').promisify;
const Articles.getPromise = promisify(Articles.get);
// load articles and save promise so we can use the promise later
const articlesP = Articles.getPromise().then(articles => {
const articleSet = new Set(articles);
return articleSet;
}).catch(err => {
console.log("Error loading article routes", err);
throw err;
});
app.use("/article/:articleSlug", function(req, res, next) {
articlesP.then(articleSet => {
// dynamically check if this route matches a known article slug
if (articleSet.has(req.param.articleSlug)) {
articleRoute(req, res, next);
} else {
// no match found, just call next()
next();
}
});
});
module.exports = app;
然后,您必须编辑articleRoute()
才能期望实际的文章名称不是路径的开头,而是req.param.articleSlug
或第二个路径元素。
注意,这还可以让您动态地添加和删除文章,而无需重新启动服务器。