电子书:http://download.csdn.net/download/qfire/10232839
代码:https://i-blog.csdnimg.cn/blog_migrate/8b1d01c3d2170aa3f6fd988903bea78c.png
第六章 请求和响应
除了请求报头外,请求还有一个主体(就像作为实际内容返回的响应主题一样)。一般GET请求没有主题内容,但POST请求是有的。POST请求体最常见的媒体类型是application/x-www-form-urlendcoded,是键值对集合的简单编码,用&分隔(基本上和查询字符串的格式一样)。如果POST请求需要支持文件上传,则媒体类型是multipart/form-data,它是一种更为复杂的格式,最后是AJAX请求,它可以使用application/json。
第七章 Handlebars模板引擎
在Node中,有许多模板引擎可供选择。Veena Basavaraj的文章关于她为LinkedIn选择模板引擎时的相关准则。
https://engineering.linkedin.com/frontend/client-side-templating-throwdown-mustache-handlebars-dustjs-and-more
理解模板引擎的关键在于context。当你渲染一个模板时,便会传递给模板引擎一个对象,叫做上下文对象,它能让替换标识运行。
服务器端模板除了隐藏实现细节,还支持模板缓存,这对性能很重要。
第八章 表单处理
从用户那里收集信息的常用方法就是使用HTML表单。
有一种普遍的误解是POST请求是安全的,而GET请求不安全。事实上如果使用HTTPS协议,两者都是安全的;如果不使用,则都不安全。如果不使用HTTPS协议,入侵者会像查看GET请求的查询字符串一样。轻松查看POST请求的报文数据。然而,如果你使用GET请求,用户会在查询字符串中看到所有的输入数据(包括隐藏域),此外,浏览器会限制查询字符串的长度。基于这些原因,一般推荐使用POST进行表单提交。
注意隐藏域:它不会呈现在浏览器中。但是,你不能使用它存放秘密和敏感信息:用户只要查看页面源文件,隐藏域就会暴露出来。
发送文件不建议使用Express处理。
Express表单处理:GET的表单域在req.query对象中,POST需要引入中间件app.use(require('body-parser')()); req.body变为可用。
$ npm install --save body-parser
var bodyparser = require('body-parser').urlencoded({extended:false});
app.use(bodyparser);
app.get('/newsletter', function(req, res) {
console.log("newsletter");
res.render('newsletter', {csrf: "CSRF token goes here" });
});
app.post('/process', function(req, res){
console.log('Form :' + req.query.form);
console.log('CSRF token :' + req.body._csrf);
console.log('Name :' + req.body.name);
console.log('Email :' + req.body.email);
//重定向thank-you视图
res.redirect(303, '/thank-you');
});
$ subl views/newsletter.handlebars
<h2>Sign up for our newsletter to receive news and specials!</h2>
<form class="form-horizontal" role="form" action="/process?form=newsletter" method="POST">
<input type="hidden" name="_csrf" value="{{csrf}}">
<div class="form-group">
<label for="fieldName" class="col-sm-2 control-label">Name</label>
<div class="col-sm-4">
<input type="text" class="form-control" id="fieldName" name="name">
</div>
</div>
<div class="form-group">
<label for="fieldEmail" class="col-sm-2 control-label">Email</label>
<div class="col-sm-4">
<input type="email" class="form-control" id="Email" name="email">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-4">
<button type="submit" class="btn btn-default">Register</button>
</div>
</div>
</form>
http://localhost:3000/newsletter
处理AJAX表单:
<h2>Sign up for our newsletter to receive news and specials!</h2>
<div class="formContainer">
<form class="form-horizontal" role="form" action="/process?form=newsletter" method="POST">
<input type="hidden" name="_csrf" value="{{csrf}}">
<div class="form-group">
<label for="fieldName" class="col-sm-2 control-label">Name</label>
<div class="col-sm-4">
<input type="text" class="form-control" id="fieldName" name="name">
</div>
</div>
<div class="form-group">
<label for="fieldEmail" class="col-sm-2 control-label">Email</label>
<div class="col-sm-4">
<input type="email" class="form-control" id="Email" name="email">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-4">
<button type="submit" class="btn btn-default">Register</button>
</div>
</div>
</form>
</div>
{{#section 'jquery'}}
<script>
$(document).ready(function(){
$('.newsletterForm').on('submit', function(evt){
evt.preventDefault();
var action = $(this).attr('action');
var $container = $(this).closest('.formContainer');
$.ajax({
url: action,
type: 'POST',
success: function(data) {
if(data.success){
$container.html('<h2>Thank you!</h2>');
} else {
$container.html('There was a problem.');
}
},
error: function() {
$container.html('There was a problem.');
}
});
});
})
</script>
{{/section}}
app.post('/process', function(req, res){
//如果是AJAX,则req.xhr为true
if (req.xhr || req.accepts('json,html') === 'json') {
//通常将数据保存到数据库
res.send({success: true});
} else {
//重定向thank-you视图
res.redirect(303, '/thank-you');
}
});
文件上传:可以使用Connect的内置中间件multipart处理。但它已经移除。对于复合表单处理,目前有两种流行而健壮的选择:Busboy和Formidable。我们使用Formidable
$ npm install --save formidable
$ subl views/contest/vacation-photo.handlebars
<form class="form-horizontal" role="form" enctype="multipart/form-data" method="POST" action="/contest/vacation-photo/{year}/{month}">
<div class="form-group">
<label for="fieldName" class="col-sm-2 control-label">Name</label>
<div class="col-sm-4">
<input type="text" class="form-control" id="fieldName" name="name">
</div>
</div>
<div class="form-group">
<label for="fieldEmail" class="col-sm-2 control-label">Email</label>
<div class="col-sm-4">
<input type="email" class="form-control" required id="fieldEmail" name="email">
</div>
</div>
<div class="form-group">
<label for="fieldPhoto" class="col-sm-2 control-label">Vacation</label>
<div class="col-sm-4">
<input type="file" class="form-control" required accept="image/*" id="fieldPhoto" name="photo">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-4">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</form
var formidable = require('formidable');
app.get('/contest/vacation-photo', function(req, res){
var now = new Date();
res.render('contest/vacation-photo', {
year: now.getFullYear(), month: now.getMonth()
});
});
app.post('/contest/vacation-photo/:year/:month', function(req, res){
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
if (err) return res.redirect(303, '/error');
console.log('received fields:');
console.log(fields);
console.log('received files:');
console.log(files);
res.redirect(303, '/thank-you');
});
});
jQuery文件上传:可拖拽、看上传文件缩略图、查看进度条。推荐使用http://blueimp.github.io/jQuery-File-Upload
安装ImageMagick,文件的存储可以是创建一个时间戳目录来存储文件。更实际的做法是使用用户ID或其他唯一ID来创建子目录。例如,如果实现一个支持文件共享的聊天程序,你可能使用聊天室的ID。