node.js基础:HTTP服务器
一个HTTP服务器响应
var http = require('http'); http.createServer(function(request,response){ response.end('hello world!'); }).listen(3000);
读取请求头及设定响应头
// res.setHeader(field, value) // res.getHeader(field) // res .removeHeader(field) // 默认状态码200(表明成功) res.setHeader('Content-Type','text/html'); res.writeHead(200,{'Content-Type':'text/html'});
设定HTTP响应的状态码
//当所请求的资源不存在时返回一个404 Not Found状态码 //设定res.statusCode属性。在程序响应期间可以随时给这个属性赋值,需要是在第一次调用res.write()或res.end()之前设置. res.statusCode = 302;
用POST请求创建资源
var http = require('http'); http.createServer(function(request,response){ request.setEncoding('utf-8'); request.on('data',function(chunk){ console.log( chunk ); }); request.on('end',function(){ console.log('done'); response.end('hello world!'); }); }).listen(3000); //默认情况下,data事件会提供Buffer对象,这是node.js版的字节数组 //不需要二进制数据,调用req.setEncoding(encoding)方法可以将流编码为ascii或utf8,这样data事件会给出字符串
var http = require('http'); var url = require('url'); var items = []; http.createServer(function(req, res){ switch (req.method){ case 'POST': var item = ''; req.setEncoding('utf8'); req.on('data', function(chunk){ item += chunk; }); req.on('end', function(){ items.push(item); res.end('OK\n'); }); break; } }).listen(3000);
用GET请求获取资源
var http = require('http'); var url = require('url'); var items = []; http.createServer(function(req, res){ switch (req.method){ case 'POST': //.. break; case 'GET': items.forEach(function(item,index){ res.write(index +'/'+ item +'\n'); res.end(); }); break; } }).listen(3000); //为了提高响应速度,可以在响应中带着Content-Length域一起发送 var body = items.map(function(item,index){ return i +'/'+ item; }).join('\n'); res.setHeader('Content-Length',Buffer.byteLength()); res.setHeader('Content-Type','text/plain'); res.end(body);
用DELETE请求移除资源
var http = require('http'); var url = require('url'); var items = []; http.createServer(function(req, res){ switch (req.method){ case 'POST': //.. break; case 'GET': //.. break; case 'DELETE': var path = url.parse(req.url).pathname; var i = parseInt(path.slice(1),10); if(isNaN(i)){ res.statusCode = 400; res.end('Invalid item id'); }else if(!items[i]){ res.statusCode = 404; res.end('item nod found'); }else{ items.splice(i,1); res.end('delete done'); } break; } }).listen(3000);
创建一个静态文件服务器
每个静态文件服务器都有个根目录,也就是提供文件服务的基础目录。
定义一个root变量,它将作为这个静态文件服务器的根目录。
var root = __dirname; //__dirname 的值是该文件所在目录的路径。 //如果有分散在不同目录中的文件,__dirname可以有不同的值。
var http = require('http'); var parse = require('url').parse; var join = require('path').join; var fs = require('fs'); var root = __dirname; var server = http.createServer(function(req, res){ var url = parse(req.url); var path = join(root, url.pathname); //绝对路径 var stream = fs.createReadStream(path); //高层流式硬盘访问文件内容 stream.on('data', function(chunk){ res.write(chunk); }); stream.on('end', function(){ res.end(); }); }); server.listen(3000);
优化数据传输
node.js中的管道: 是来自源头(即ReadableStream)的数据,管道可以让数据流动到某个目的地(即WritableStream),可以用pipe方法把管道连起来。
var readStream = fs.createReadStream('./original.txt') var writeStream = fs.createWriteStream('./copy.txt') readStream.pipe(writeStream); //... var server = http.createServer(function(req, res){ var url = parse(req.url); var path = join(root, url.pathname); //绝对路径 var stream = fs.createReadStream(path); //高层流式硬盘访问文件内容 stream.pipe(res); }); //...
处理服务器错误
如果访问不存在的文件,或者不允许访问的文件,或者碰到任何与文件I/O有关的问题,当前的服务器会抛出错误。
在node.js中,所有继承了EventEmitter的类都可能会发出error事件。
默认情况下,如果没有设置监听器,error事件会被抛出。也就是说如果你不监听这些错误,那它们就会搞垮服务器。
注册一个error事件处理器,可以捕获任何可以预见或无法预见的错误,给客户端更优雅的响应。
//.. stream.pipe(res); stream.on('error',function(error){ res.statusCode = 500; res.end('Invalid Server Error'); }); //..
一个简单的支持GET、POST的HTTP服务器
模板文件 template.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>A HTTP Server</title> </head> <body> <ul>%</ul> <form method="POST" action="/"> <p><input type="text" name="item" value="" /></p> <p><input type="submit" value="Add item" /></p> </form> </body> </html>
node.js文件 index.js
var http = require('http'); var fs = require('fs'); var qs = require('querystring'); var items = []; var server = http.createServer(function(req, res){ if(req.url == '/'){ switch (req.method.toUpperCase()){ case 'GET': show(res); break; case 'POST': add(req, res); break; default: badRequest(res); } }else{ notFound(res); } }); server.listen(3000); function show(res){ fs.readFile('./template.html',function(err, data){ var html = data.toString().replace('%', items.map(function(item,index){ return '<li>'+ item +'</li>'; }).join('')); res.setHeader('Content-Type','text/html'); res.setHeader('Content-Length',Buffer.byteLength(html)); res.end(html); }); } function add(req, res){ var body = ''; req.setEncoding('utf8'); req.on('data',function(chunk){ body += chunk; }); req.on('end',function(){ items.push( qs.parse(body).item ); show(res); }); } function notFound(res){ res.statusCode = 404; res.setHeader('Content-Type','text/plain'); res.end('Not Found'); } function badRequest(res){ res.statusCode = 400; res.setHeader('Content-Type','text/plain'); res.end('Bad Request'); }
一个文件上传HTTP服务器
上传文件需要把表单的enctype属性设为multipart/form-data。
formidable模块可以高效流畅的方式解析文件上传请求。
html模板文件: template.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>upload file</title> </head> <body> <form method="POST" action="/" enctype="multipart/form-data"> <p><input type="text" name="name"/></p> <p><input type="file" name="file"/></p> <p><input type="submit" value="upload file" /></p> </form> </body> </html>
node.js文件:index.js
var http = require('http'); var fs = require('fs'); var qs = require('querystring'); var formidable = require('formidable') var items = []; var server = http.createServer(function(req, res){ if(req.url == '/'){ switch (req.method.toUpperCase()){ case 'GET': show(res); break; case 'POST': upload(req, res); break; default: badRequest(res); } }else{ notFound(res); } }); server.listen(3000); function show(res){ fs.readFile('./template.html',function(err, data){ var html = data.toString(); res.setHeader('Content-Type','text/html'); res.setHeader('Content-Length',Buffer.byteLength(html)); res.end(html); }); } function upload(req,res){ if(!isFormData(req)){ return badRequest(res); } var form = formidable.IncomingForm(); form.uploadDir = __dirname; //路径设置 // form.on('field',function(field,value){ // console.log(field); // console.log(value); // }); // form.on('file',function(name,file){ // console.log(name); // console.log(file); // }); // form.on('end',function(){ // res.end('upload complate'); // }); // form.parse(req); //上传进度事件 form.on('progress',function(bytesReceived,bytesExpected){ var progress = Math.floor( bytesReceived/bytesExpected*100 ); console.log(progress); }); form.parse(req,function(err,fields,files){ console.log(fields); console.log(files); //console.log( files.file.path ); res.end('upload complate'); }); } function isFormData(req){ var type = req.headers['content-type'] || ''; return type.indexOf('multipart/form-data') == 0; } function notFound(res){ res.statusCode = 404; res.setHeader('Content-Type','text/plain'); res.end('Not Found'); } function badRequest(res){ res.statusCode = 400; res.setHeader('Content-Type','text/plain'); res.end('Bad Request'); }
用https加强程序的安全性
如果想在node.js程序里使用https,第一件事就是取得一个私钥和一份证书。
生成私钥需要OpenSSL,在装node.js时就已经安装了。
生成名为key.pem的私钥文件, 在项目根目录输入命令:openssl genrsa 1024 > key.pem
生成名为key-cert.pem的证书,在项目根目录输入命令:openssl req -x509 -new -key key.pem > key-cert.pem
var https = require('https'); var fs = require('fs'); var options = { key: fs.readFileSync('./key.pem'), cert: fs.readFileSync('./key-cert.pem') }; https.createServer(options,function(req,res){ res.writeHead(200); res.end('hello node.js!'); }).listen(3000);