用Node.js 写web框架(三)

补充1:
一中的设计显然缺少了日志系统,这里先补充一个简单的,日后再完善:
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'
};
这个并不全,不够暂时够用了。标准参考: 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;
	}
}
好了,大功告成。

你可能感兴趣的:(用Node.js 写web框架(三))