我正在寻找一种在Node.js (Express)中提供模板的方法,两者"正常"呈现为HTML并呈现为JSON,如果它是用ajax请求的。
假设我在swig中有一个这样的模板:
{% extends 'layout.html' %}
{% block title %}Default Page{% endblock %}
{% block body %}
<p>Hi, {{ name }}.</p>
{% endblock %}
现在,如果我用浏览器发出一个正常的请求,swig将以layout.html
作为布局呈现文件。但是,如果我用ajax(或参数像?partial
)的请求,我想有块JSON,没有它被渲染在layout.html
:
{
"title": "Default Page",
"body": "<p>Hi, Dave.</p>"
}
swig的选择是任意的,它可以是任何支持布局继承与块的视图引擎。
我在swig和Nunjucks的文档中搜索了一个简单的方法来影响基于请求的模板渲染,但我没有想到一个想法(没有它是一个完全多余的)。
根据您的具体用例简化我的回答:
您可以通过使用"中间件覆盖"轻松实现这一点。
app.use(function (req, res, next) {
var oldRender = res.render.bind(res);
res.render = function (viewName, viewData) {
viewData.xhr = req.xhr;
if (!req.xhr) {
res.render(viewName, viewData);
} else {
res.render(viewName, viewData, function (err, renderedView) {
if (err) return next(err);
res.json({
url: req.url, // In case you want to use the HTML5 history API to update the URL in-place in the client's browser.
title: viewData.title,
view: renderedView
});
});
}
};
});
理想情况下,你的视图引擎应该能够做这样的事情:
{% if !xhr %}
{% extends 'layout.html' %}
{% endif %}
现在当你调用res.render('someView', { title: 'some title' });
时,它将编译一个完整的视图并将其发送下来,或者如果它是一个XHR请求,那么它将发送一个JSON blob,其中包含请求URL,页面标题和呈现的视图作为字符串。然后,您可以在客户端上动态更新页面。
如果你的模板引擎不支持条件extends
,那么你可以用一点"hack"来解决这个问题。您将需要两个视图(伪代码):
// someView
<p>Hi, {{ name }}</p>
// someView_full
{% extends 'layout.html' %}
{% block title %}Default Page{% endblock %}
{% block body %}
{% include 'someView' %}
{% endblock %}
然后修改您的中间件函数,当它不是AJAX请求时,将"_full"
附加到视图名称。
if (!req.xhr) {
res.render(viewName + "_full", viewData);
} else { ... }
Edit:还有另一种方法可以避免创建第二个小桥视图,甚至必须将每个extends
包装在每个视图的条件中。我在这里找到了这个方法。原来,您可以有条件地在布局文件中创建内容块,使之成为可能:
// layout
{% if !xhr %}
<html>
<head>
<title>My Awesome Page</title>
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>
{% else %}
{% block body %}{% endblock %}
{% endif %}
这样布局文件将决定是否包含周围的标记。
PS - req.xhr
只工作,如果客户端框架使AJAX请求包括x-requested-with
头设置为"XMLHttpRequest"(提示:大多数)。如果你的框架不包含该头文件,那么你可以切换一个选项,或者至少你应该能够设置自定义头文件并自己包含它。
您可以像您在问题中建议的那样使用查询字符串变量,但是使用此请求标头是与服务器通信的标准约定,该请求是AJAX的,并且通常在客户端框架中默认打开。这就是为什么Express有req.xhr
内置:)