简介
一般网站的404页面包括两种类型:
1.路由匹配失败,没有此路由;
2.路由匹配成功,但参数不对,没有数据显示。
对于第一种情况,flow-router和iron-router都能轻松的处理,但对于第二种情况,iron-router可以设置dataNotFound的hook,而flow-router文档中并没有提及。本文就是对这种情况的总结。
场景还原
例如,在imports/startup/routes.js中,我定义了下面的路由,用于显示某个内容的详细信息:
1 | FlowRouter.route('/detail/:queryId',{ |
其中参数:queryId对应于Mongo数据库中的_id字段,当_id不存在时,我需要显示404页面,而不再渲染此页面的detail模板。
其中BlazeLayout需要添加kadira:blaze-layout包:
1 | #终端执行 |
具体用法请看文档
第一次尝试
为了检查:queryId是否存在,我想到了flow-router中的triggersEnter方法,当执行此路由时,先检查数据是否存在,如果不存在,就直接渲染notFound,并且停止路由中action方法的执行,如下所示:
1 | FlowRouter.route('/detail/:queryId',{ |
其中notExist函数共有3个参数,context必须,redirect可以没有,stop此方法必须。
从context中,我们可以得到其id,然后通过查询数据库,检查返回的数据是否为空,如果为空,就执行BlazeLayout.render('notFound');,并且停止路由的下一步进行。
其中isEmpty来源于lodash库:
1 | #终端执行 |
分析结果
看起来逻辑没有错误,但是运行起来发现,当刷新页面时,就会显示404,而且浏览器终端也出现错误,通过console.log()对数据返回值的检查,发现Resources.findOne({_id: id})一直返回空对象,表示在route中数据库不起作用。
第二次尝试
在路由中没有成功,我准备在模板imports/ui/template/detail.js中再次尝试,如下:
1 | #在onCreated方法中订阅 |
通过FlowRouter.getParam得到id,然后数据库查找,如果数据库为空,显示404页面;如果不为空,储存返回值detailContent到ReactiveDict中,用于在模板中获取内容。
没想到,这次成功了,看来在路由文件声明中,不能使用数据库的查找。
小的改进
上面代码中,我使用了FlowRouter.go('/404'),这样就必须定义一个新的路由,没有必要,并且,浏览器地址栏就会变成如www.example.com/404;而如果使用BlazeLayout.render('notFound'),浏览器地址不会改变,如www.example.com/detail/ddddddd。
可以直接改为:
1 | if (isEmpty(detailContent)) { |
但是有一个问题,刷新页面时,会闪过mainLayout布局文件的内容,如navbar,需要继续改进:
1 | if (isEmpty(detailContent)) { |
这里,我新建了一个模板文件dataNotFound,因为notFound模板样式与mainLayout的样式不一定兼容,最好是分开。
这样,对于文章开头的两种情况:
- notFound 对应于第一种,显示全局的404页面
- dataNotFound 对应于第二种,显示数据没有找到的404页面
小的问题
在Template.detail.helpers()中,不止一个helper,比如我还定义了isImage方法,用于检查类型是否为图片:
1 | isImage(type) { |
但是在浏览器终端,就会出现这样的错误信息:

原因出在哪,不清楚,但使用lodash中的startsWith方法替代后,错误信息就没了。
1 | #首先引入 |
最后说明
Meteor版本: 1.4.1.1
Flow-router版本: 2.12.1
Meteor从1.3版本开始,就能使用npm包了,并且项目结构有所改变,比如在detail.js文件开头,使用ES6语法来引入可能需要的包:
1 | #使用meteor add添加的包 |

