内容渲染
大多数情况下,渲染内容用 res.render,它最大程度地根据布局渲染视图。如果想写一个快速测试页,也许会用到 res.send。你可以使用 req.query 得到查询字符串的值,使用req.session 得到会话值,或使用req.cookie/req.singedCookies 得到 cookies 值。
到示例 6-8 演示了常见的内容渲染任务:
示例 6-1 基本用法
// 基本用法
app.get('/about', function(req, res){
res.render('about');
});
示例 6-2 200 以外的响应代码
app.get('/error', function(req, res){
res.status(500);
res.render('error');
});
// 或是一行……
app.get('/error', function(req, res){
res.status(500).render('error');
});
示例 6-3 将上下文传递给视图,包括查询字符串、cookie 和 session 值
app.get('/greeting', function(req, res){
res.render('about', {
message: 'welcome',
style: req.query.style,
userid: req.cookie.userid,
username: req.session.username,
});
});
示例 6-4 没有布局的视图渲染
// 下面的 layout 没有布局文件,即 views/no-layout.handlebars
// 必须包含必要的 HTML
app.get('/no-layout', function(req, res){
res.render('no-layout', { layout: null });
});
示例 6-5 使用定制布局渲染视图
// 使用布局文件 views/layouts/custom.handlebars
请求和响应对象 | 57
app.get('/custom-layout', function(req, res){
res.render('custom-layout', { layout: 'custom' });
});
示例 6-6 渲染纯文本输出
app.get('/test', function(req, res){
res.type('text/plain');
res.send('this is a test');
});
示例 6-7 添加错误处理程序
// 这应该出现在所有路由方法的结尾
// 需要注意的是,即使你不需要一个 " 下一步 " 方法
// 它也必须包含,以便 Express 将它识别为一个错误处理程序
app.use(function(err, req, res, next){
console.error(err.stack);
res.status(500).render('error');
});
示例 6-8 添加一个 404 处理程序
// 这应该出现在所有路由方法的结尾
app.use(function(req, res){
res.status(404).render('not-found');
});
6.11.2 处理表单
当你处理表单时,表单信息一般在 req.body 中(或者偶尔在 req.query 中)。你可以使用
req.xhr 来判断是 AJAX 请求还是浏览请求(第 8 章将深入讨论)。让我们看看示例 6-9 和
示例 6-10。
示例 6-9 基本表单处理
// 必须引入中间件 body-parser
app.post('/process-contact', function(req, res){
console.log('Received contact from ' + req.body.name +
' <' + req.body.email + '>');
// 保存到数据库……
res.redirect(303, '/thank-you');
});
示例 6-10 更强大的表单处理
// 必须引入中间件 body-parser
app.post('/process-contact', function(req, res){
console.log('Received contact from ' + req.body.name +
' <' + req.body.email + '>');
try {
// 保存到数据库……
return res.xhr ?
58 | 第6章
res.render({ success: true }) :
res.redirect(303, '/thank-you');
} catch(ex) {
return res.xhr ?
res.json({ error: 'Database error.' }) :
res.redirect(303, '/database-error');
}
});
6.11.3 提供一个API
如果提供一个类似于表单处理的 API,参数通常会在 req.query 中,虽然也可以使用 req.
body。与其他 API 不同,这种情况下通常会返回 JSON、XML 或纯文本,而不是 HTML。
你会经常使用不太常见的 HTTP 方法,比如 PUT、POST 和 DELETE。提供 API 将在第 15 章
深入讨论。示例 6-11 和示例 6-12 使用下面的“产品”数组(通常是从数据库中检索):
var tours = [
{ id: 0, name: 'Hood River', price: 99.99 },
{ id: 1, name: 'Oregon Coast', price: 149.95 },
];
“节点”一词经常用于描述 API 中的单个方法。
示例 6-11 简单的 GET 节点,只返回 JSON 数据
app.get('/api/tours'), function(req, res){
res.json(tours);
});
示例 6-12 根据客户端的首选项,使用 Express 中的 res.format 方法对其响应。
示例 6-12 GET 节点,返回 JSON、XML 或 text
app.get('/api/tours', function(req, res){
var toursXml = '' +
products.map(function(p){
return '' + p.name + '';
}).join('') + ''';
var toursText = tours.map(function(p){
return p.id + ': ' + p.name + ' (' + p.price + ')';
}).join('\n');
res.format({
'application/json': function(){
res.json(tours);
},
'application/xml': function(){
请求和响应对象 | 59
res.type('application/xml');
res.send(toursXml);
},
'text/xml': function(){
res.type('text/xml');
res.send(toursXml);
}
'text/plain': function(){
res.type('text/plain');
res.send(toursXml);
}
});
});
在示例 6-13 中,PUT 节点更新一个产品信息然后返回 JSON。参数在查询字符串中传递
(路由字符串中的 '':id'' 命令 Express 在 req.params 中增加一个 id 属性)。
示例 6-13 用于更新的 PUT 节点
//API 用于更新一条数据并且返回 JSON;参数在查询字符串中传递
app.put('/api/tour/:id', function(req, res){
var p = tours.some(function(p){ return p.id == req.params.id });
if( p ) {
if( req.query.name ) p.name = req.query.name;
if( req.query.price ) p.price = req.query.price;
res.json({success: true});
} else {
res.json({error: 'No such tour exists.'});
}
});
最后,示例 6-14 展示了一个 DEL 节点。
示例 6-14 用于删除的 DEL 节点
// API 用于删除一个产品
api.del('/api/tour/:id', function(req, res){
var i;
for( var i=tours.length-1; i>=0; i-- )
if( tours[i].id == req.params.id ) break;
if( i>=0 ) {
tours.splice(i, 1);
res.json({success: true});
} else {
res.json({error: 'No such tour exists.'});
}
});