一、web服务器示例
var http = require('http'); http.createServer(function(req, res){ res.writeHeader(200, {Content-Type : 'text/plain'}); res.end('hello world!'); }).listen(80);
二、web应用中常见需求:
(1)解析请求方法(POST GET)
(2)解析路径
(3)解析查询字符串
(4)解析cookie
(5)Basic认证
(6)解析表单数据
(7)文件上传
三、解析请求方法
function(req, res){ var method = req.method; if(method === 'POST'){ }else if(method === 'GET'){ }else if(method === 'DELETE'){ }else if(method === 'PUT'){ }else{ } }
四、解析路径
(1)返回静态资源
var url = require('url'); var fs = require('fs'); function(req, res){ var pathname = url.parse(req.url).pathname; fs.readFile(path.join(ROOT, pathname), function(err, file){ if(err){ res.writeHeader(404); res.end('not Found File'); return; } res.writeHeader(200); res.end(file); }); }
(2)选择控制器:/controller/action/a/b/c
function(req, res){ var pathname = url.parse(req.url).pathname; var paths = pathname.split('/'); var contronller = paths[1] || 'index'; var action = paths[2] || 'index'; var args = paths.slice(3); if(handler[contronller] && handler[contronller][action]){ handler[contronller][action].apply(null, [req, res].concat(args)); }else{ res.writeHeader(500); res.end('找不到响应控制器'); } }
四、查询字符串
var url = require('url'); var query = url.parse(req.url, true).query;//{foo : 'aaa', baz : 'bbb'} req.query = query; handle(req, res);
五、cookie
function parseCookie(cookie){ var cookies = {}; if(!cookie){ return cookies; } var list = cookie.split(';'); for(var i = 0,len = list.length;i < len;i++){ var pair = list[i].split('='); cookies[pair[0].trim()] = pair[1]; } return cookies; }; req.cookies = parseCookie(req.headers.cookie); handle(req, res); function serialize(name, value, options){ var pairs = [name+'='+value]; if(options.expires){ pairs.push('Expires='+options.expires.toUTCString()); } if(options.maxAge){ pairs.push('Max-Age='+options.maxAge); } if(options.path){ pairs.push('Path='+options.path); } if(options.domain){ pairs.push('Domain='+options.domain); } if(options.httpOnly){ pairs.push('HTTPOnly'); } if(options.secure){ pairs.push('Secure'); } return pares.join(';'); } res.writeHeader('Set-Cookie', serialize({isVisit : 1}));
六、Session
Cookie缺点:
(1)可在前端改变cookie的值(HTTPOnly可限制前端改变cookie),易篡改。
(2)隐私信息无法存cookie
(3)占用网络带宽(每次请求,相关path下的cookie信息都会在请求头中携带,虽然服务端有时候并不需要所有的cookie信息)
基于以上缺点,敏感信息必须从Session获取。
在服务端为Session生成一个键值,将该键值写入Cookie,用户凭借键值获取自己的session 用于解决http协议无状态缺陷,维护用户状态。
七、数据上传
var hasBody = function(req){ return 'transfer-encoding' in req.Headers || 'content-length' in req.Headers; } function(req, res){ if(hasBody(req)){ var buffers = []; req.on('data', function(chunk){ buffers.push(chunk); }); req.on('end', function(){ req.rawBody = Buffer.concat(buffers).toString(); handle(req, res); }); }else{ handle(req, res); } };
业务中可以判断conten-type 采用不同的方式解析req.rawBody
x-www-form-urlencoded:
req.body = querystring.parse(req.rawBody); var mime = function(req){ return req.headers['content-type'].split(';')[0] || ''; } var xml2js = require('xml2js'); var handle = function(req, res){ if(mime(req) === 'application/json'){ try{ req.body = JSON.parse(req.rowBody); }catch(){ res.writeHeader('400'); res.end('invalid json'); return; } } if(mime(req) === 'application/xml'){ xml2js.parseString(req,rawBody, function(err, xml){ if(err){ }else{ req.body = xml; } }); } };
附件上传:
content-type : multipart/form-data
var formidable = require('formidable'); function(req, res){ if(hasBody(req)){ if(mime(req) === 'multipart/form-data'){ var form = new formidable.IncomingForm(); form.parse(req, function(err, fields, files){ req.body = fields; req.files = files; handle(req, res); }); } } }
八、数据上传与安全
在解析表单,JSON和XML的过程中,如果用户提交的数据量较大,不能直接读入内存。避免内存耗尽的情况发生。一般解决这种问题有以下两种方案:
(1)、限制上传内容的大小,超过大小直接返回400状态码
(2)、流式解析,将数据导向磁盘中,Node中只保留文件路径。
如下为Connect中采用的上传数据量的限制方式:
var bytes = 1024; function(req, res){ var received = 0; var len = req.headers['content-length']?parseInt(req.headers['content-length'], 10):null; if(len > bytes){ res.writeHeader(413); res.end; return; } res.on('data', function(chunk){ received += chunk.length; if(received > bytes){ req.destroy(); } }); handle(req, res); };
CSRF 跨站消息伪造
防御方法:服务端生成随机数,将该随机数告知前端,前端在请求中携带该随机数。没有携带制定规则随机数的请求服务端一律不处理。
九、附件下载
设置响应头Content-Disposition : attachment;filename="fdfd.txt"