补充1:
一中的设计显然缺少了日志系统,这里先补充一个简单的,日后再完善:
logger.js
exports.setDevMode = function(mode){ devMode = mode; } exports.log = function(msg){ if(devMode){ console.log(msg); } } var devMode = true;补充2:
补充3:
说一下当前目录的结构和所有文件:
/server.js---------------------------主文件(其实啥也没有,就是启动服务器,绑定Dispatcher)
/dispatcher.js---------------------Dispathcer逻辑部分
/logger.js---------------------------日志部分
/utils.js------------------------------暂时将一些公用方法放在这,以后可能单独整理一个库
/viewHelper.js--------------------渲染库,应包括:渲染模板,直接返回单独文件等
---------------------以上最后应挪入一个单独的模块包中,成为web框架部分--------------------------
/views.js----------------------------根目录views文件,参见系列二中的介绍
/modules----------------------------模块目录
/modules/article------------------模块article的目录(测试用,以后会完善)
/modules/article/views.js-----模块article的views文件
实际上在设计中参考了一些Django的组织结构。
下面是正文,今天实现的是静态文件系列。逻辑不复杂,不过内容很多。
当一个静态文件请求到来(比如说请求xxx.js/xxx.css/xxx.html或者xxx.mp3等等),检查静态文件路径中是否包含这个静态文件,然后返回就可以了。
这里为了浏览器能够正确的打开静态文件,需要在返回时写MIME type头,所以需要一个MIME type表:
var STATIC_TYPES = { 'css' : 'text/css', 'gif' : 'image/gif', 'html' : 'text/html', 'ico' : 'image/x-icon', 'jpeg' : 'image/jpeg', 'jpg' : 'image/jpeg', 'js' : 'text/javascript', 'json' : 'application/json', 'mp3' : 'audio/mpeg', 'pdf' : 'application/pdf', 'png' : 'image/png', 'svg' : 'image/svg+xml', 'swf' : 'application/x-shockwave-flash', 'tiff' : 'image/tiff', 'txt' : 'text/plain', 'wav' : 'audio/x-wav', 'wma' : 'audio/x-ms-wma', 'wmv' : 'video/x-ms-wmv', 'xml' : 'text/xml' };这个并不全,不够暂时够用了。标准参考: http://www.iana.org/assignments/media-types/index.html
下面就是检查request所需要的文件的后缀了,这个简单,一个正则就搞定了。
然后检查文件是否存在,如果存在就返回文件,否则就执行下一步就可以了。
代码:
/** * Try to response a static file. */ function handleStatics(req, res){ // check whether request has extension. var extension = req.url.match(/\.\w+$/); if(!extension){ return false; } var path = './' + option.staticPath + req.url; if(fs.existsSync(path)){ fs.readFile(path, 'binary', function(err, file){ if(err){ throw err; }else{ var contentType = STATIC_TYPES[extension.slice(1)] || 'text/plain'; res.writeHead(200, {ContentType : contentType}); res.write(file, 'binary'); res.end(); } }); return true; }else{ return false; } }这里检查文件是否存在是同步的,以后看情况考虑是否要换成异步操作(这种的话异步很蛋疼...)
下面写一个文件试验一下吧:
/static/test.html
<!Doctype html> <html> <head> <meta charset="utf-8"> <title>test</title> </head> <body> <h1>Hello!</h1> </body> </html>然后开启服务器,试验一下:localhost:8080/test.html,ok成功。
这里熟悉HTTP协议的人估计就要喷我了,因为完全没有缓存文件。HTTP状态嘛中,有一个专门的状态码304,用于告知客户端这个文件并没有改变,直接使用客户端缓存就可以了。
这里就要在HTTP头上加入Expires、Cache-Control和Last-Modified这三个来标识缓存时间和并检查文件是否已经被修改。
/** * Try to response a static file. */ function handleStatics(req, res){ // check whether request has extension. var extension = req.url.match(/\.\w+$/); if(!extension){ return false; } var path = './' + option.staticPath + req.url; if(fs.existsSync(path)){ fs.readFile(path, 'binary', function(err, file){ if(err){ throw err; }else{ var lastModified = fs.statSync(path).mtime.toUTCString(); if(req.headers['If-Modified-Since'] && lastModified == req.headers['If-Modified-Since']){ res.writeHead(304, "Not Modified"); res.end(); }else{ var contentType = STATIC_TYPES[extension.slice(1)] || 'text/plain'; var expires = new Date(new Date().getTime() + option.maxAge).toUTCString(); var cacheControl = 'max-age=' + option.maxAge / 1000; res.writeHead( 200, { 'ContentType' : contentType, 'Expires' : expires, 'Cache-Control' : cacheControl, 'Last-Modified' : lastModified }); res.write(file, 'binary'); res.end(); } } }); return true; }else{ return false; } }好了,大功告成。