KeystoneJS中间件使用Model.find()调用MongoDB时运行两次

我有一个路线,加载一个公寓列表,并显示它们:

app.get( '/condo-list', middleware.loadCondoList, routes.views.condolist ); 

loadCondoList中间件调用CondoBuilding模型并在res.locals上设置结果:

 exports.loadCondoList = function loadCondoList( req, res, next ) { console.log( 'request url: ' + req.url ); console.log( 'getting condo buildings...' ); CondoBuilding.model .find() .exec( ( err, condos ) => { if ( err ) { // pass error along next( err ); } else { // add CondoBuildings to locals res.locals.condoBuildings = condos; next(); } }); }; 

对数据库的调用是成功的,页面按预期呈现。 但是,由于某种原因,路线运行了两次。 控制台输出如下:

 request url: /condo-list getting condo buildings... GET /condo-list 304 344.532 ms request url: /condo-list getting condo buildings... GET /condo-list 304 317.631 ms 

我已经在多个浏览器(Chrome浏览器,Safari浏览器,Firefox)中复制了这种行为,并validation了这不会发生在任何其他路线上。

如果我删除对CondoBuilding.model.find()的调用,只是在loadCondoList()的主体中调用next() ,则不会发生此行为。

我正在使用Express 4 "express": "4.14.0"运行Keystone 4 "keystone": "4.0.0-beta.5" "express": "4.14.0"

下面是我在应用程序中运行的路线的完整列表,以防万一:

 // Setup Route Bindings exports = module.exports = function ( app ) { // Views app.get( '/', routes.views.index ); app.get( '/condo-list', middleware.loadCondoList, routes.views.condolist ); app.get( '/blog/:category?', routes.views.blog ); app.get( '/blog/post/:post', routes.views.post ); app.get( '/about', routes.views.about ); app.get( '/search', middleware.getAccountType, routes.views.search ); app.all( '/contact', routes.views.contact ); }; 

CondoList视图:

 var keystone = require('keystone'); exports = module.exports = function (req, res) { var view = new keystone.View(req, res); var locals = res.locals; // locals.section is used to set the currently selected // item in the header navigation. locals.section = 'condolist'; // Render the view view.render('condolist'); }; 

我一直在debugging这个问题一段时间,但我在我的智慧结束可能是什么原因导致这一点。 任何帮助,将不胜感激。

UPDATE

我遵循@ phuhgh的build议,在Expressdebugging模式下运行应用程序。 虽然没有立即跳出来,我在应用程序启动过程中注意到一些奇怪的事情。

以下是一些准备正常运行的路线:

 express:router:layer new / +0ms express:router:route new /blog/post/:post +0ms express:router:layer new /blog/post/:post +0ms express:router:route get /blog/post/:post +0ms express:router:layer new / +0ms express:router:route new /about +0ms express:router:layer new /about +0ms express:router:route get /about +0ms express:router:layer new / +0ms express:router:route new /search +1ms express:router:layer new /search +0ms express:router:route get /search +0ms 

以下是正在准备的公寓列表路线的顺序:

 express:router:layer new / +0ms express:router:route new /condo-list +0ms express:router:layer new /condo-list +0ms express:router:route get /condo-list +0ms express:router:layer new / +0ms express:router:route get /condo-list +0ms 

你可能会注意到, express:router:route get /condo-list +0ms行是重复的。 我不知道为什么,但我认为这与我遇到的问题有关。 我正在深入挖掘这个angular度,但是再一次,任何有这方面知识的人的帮助都将不胜感激。

更新2 – 堆栈跟踪

堆栈跟踪

我已经debugging,一步一步地通过堆栈。 我可以按照从function到function的path,一切看起来都正常,但是我的正常基线正在寻找其他正常工作的路线。 一旦我深入到Express的内心,我真的不知道该找什么。

我在观察过程中所做的观察:

  • 堆栈跟踪与/condo-list路由运行时完全相同。
  • 堆栈跟踪(当然不包括loadCondoList中间件)对于正常运行的其他路由(即只有一次)是完全相同的。
  • 如果我添加到另一个路由loadCondoList调用,它也正常运行。
    • 例如,我更新了/about路由定义如下: app.get( '/about', middleware.loadCondoList, routes.views.about ); 并正确加载数据,只运行一次。

有没有什么我应该特别注意,因为我正在逐步通过Express lib代码? 我感觉有点深,我不知道该找什么。

经过几天的debugging,我终于find了罪魁祸首,它藏在最不可能的地方:视图!

上下文

视图加载可以被邻域过滤的公寓build筑物的砌体显示。 以下是砌体本身的相关片段:

 <!-- Condo List Masonry --> <div class="condo-items"> {{#each condoBuildings}} <div class="condo-item {{neighborhood.key}}"> <div class="condo-thumb"> <span class="condo-tag tag-art">{{neighborhood.name}}</span> <a href="/{{condoUrl}}"><img src="{{{cloudinaryUrl image}}}" alt="{{name}}" /></a> </div> <div class="condo-body"> <h3><a class="condo-name" href="#">{{name}}</a></h3> <p>{{condoDescription}}</p> </div> </div> {{/each}} </div> 

这个问题是由这个行的cloudinaryUrl助手造成的:

<a href="/{{condoUrl}}"><img src="{{{cloudinaryUrl image}}}" alt="{{name}}" /></a>

这是帮手代码:

 _helpers.cloudinaryUrl = function (context, options) { // if we dont pass in a context and just kwargs // then `this` refers to our default scope block and kwargs // are stored in context.hash if (!options && context.hasOwnProperty('hash')) { // strategy is to place context kwargs into options options = context; // bind our default inherited scope into context context = this; } // safe guard to ensure context is never null context = context === null ? undefined : context; if ((context) && (context.public_id)) { options.hash.secure = keystone.get('cloudinary secure') || false; var imageName = context.public_id.concat('.', context.format); return cloudinary.url(imageName, options.hash); } else { return null; } }; 

问题

一些CondoBuilding模型还没有定义image 。 这会导致_helpers.cloudinaryUrl方法中的context参数undefined 。 在这些情况下,助手将返回null 。 我仍然不确定为什么这会导致页面重新载入,但我确信这是罪魁祸首。

修正

如果CondoBuilding模型中存在图像,则更新Handlebars模板以仅呈现<img>元素。 更新的模板代码如下所示:

 <!-- Condo List Masonry --> <div class="condo-items"> {{#each condoBuildings}} <div class="condo-item {{neighborhood.key}}"> <div class="condo-thumb"> <span class="condo-tag tag-art">{{neighborhood.name}}</span> <a href="/{{condoUrl}}">{{#if image}}<img src="{{{cloudinaryUrl image}}}" alt="{{name}}" />{{/if}}</a> </div> <div class="condo-body"> <h3><a class="condo-name" href="#">{{name}}</a></h3> <p>{{condoDescription}}</p> </div> </div> {{/each}} 

通过在模板中添加{{#if image}}块,路线只运行一次,正如所料!

下一步

对此实现的改进是对所有没有定义图像的CondoBuilding使用占位符图像。 我很快就会添加这个function,但是我忍不住更新了一个答案,因为我几天来一直在反驳这个问题。

感谢大家的时间和关注。