一中的设计显然缺少了日志系统,这里先补充一个简单的,日后再完善:
logger.js
exports.setDevMode = function(mode){
devMode = mode;
}
exports.log = function(msg){
if(devMode){
console.log(msg);
}
}
var devMode = true;
补充2:
完善了一下Dispatcher的逻辑,对于' /aaa/bbb'情况,如果未找到对应的./modules/aaa/views.js中的bbb()函数,则查找./views.js中的aaa()函数,并把'bbb'当做第一个参数传入该函数。
补充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'
};
下面就是检查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;
}
}
好了,大功告成。