用Node.js 写Web框架(3)

一中的设计显然缺少了日志系统,这里先补充一个简单的,日后再完善:

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;
 }
}

好了,大功告成。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/43587db0d6f3ac5eaf92ab20323a50b1.html