通常在使用網站時,通常只會輸入目錄,而不是index.html,所以要讓伺服器可以支援目錄預設的index檔,功能才比較完整。
在Apache httpd中的做法
在Apache中,要設定目錄預設的index檔,通常是:
<IfModule dir_module>
DirectoryIndex index.php index.html index.htm
</IfModule>
設定好了以後,如果客戶端請求的資源是一個目錄,他會依照上面的順序看看有沒有這些檔案,如果不存在,又不允許做目錄瀏覽,就會出現Permission denied.的錯誤訊息。為了簡化問題,我會傾向如果沒有這些檔案存在,就直接出現Permission denied.。(通常也會關掉以確保安全)
在node.js實做
簡單的邏輯,是先用fs.stat()判斷request的資源是否為目錄,如果是目錄,就依照設定的index檔順序尋找,找到了就response這個檔案的內容。
如果不是目錄,就利用fs.readFile尋找檔案,找到檔案就reponse這個檔案的內容。
fs.stat()以及fs.readFile()都是使用非同步的方式操作,需要傳一個callback函數給他。另外,為了顧及可能的狀況,所以程式膨脹許多:
var http = require('http'),
fs = require('fs'),
url = require('url'),
mime = require('../deps/mime'),
path = require('path'),
evolve = function(conf) {//利用一個constructor包裝
var server = http.createServer(function(request, response) {
var urlObj = url.parse(request.url);
var respath = path.join(conf.basedir, urlObj.pathname);
console.log('request: ' + respath);
fs.stat(respath, function(err, stats) {//請求資源路徑的stats
if(err) {
console.log(respath + ' not exists.');
response.writeHead(404, '');
response.end();
} else {
if(stats.isDirectory()) {//如果是目錄,則處理index檔
var ic=0;
fs.readFile(path.join(respath, conf.dirindex[ic]), function dih(err, data) {
ic++;
if(err) {
if(ic<conf.dirindex.length) {
fs.readFile(path.join(respath, conf.dirindex[ic]), dih);
} else {
console.log(path.join(respath, conf.dirindex[ic-1]) + ' not exists.');
response.writeHead(404, '');
response.end();
}
} else {
console.log(path.join(respath, conf.dirindex[ic-1]) + ' exists.');
response.writeHead(200, {
"Content-Type": mime.lookup(path.join(respath, conf.dirindex[ic-1])),
"Content-Length": data.length
});
response.end(data);
}
});
} else {//不是目錄,開始處理檔案
fs.readFile(respath, function(err, data) {
if(err) {
console.log(respath + ' not exists.');
response.writeHead(404, '');
response.end();
} else {
console.log(respath + ' exists.');
response.writeHead(200, {
"Content-Type": mime.lookup(respath),
"Content-Length": data.length
});
response.end(data);
}
});
}
}
});
});
this.listen = function(port, addr) {//wrap函數,把listen轉給server
server.listen(port, addr);
return this;
};
};
var instance = new evolve({//利用設定參數實體化
basedir: path.join(__dirname, '/../www'),
dirindex: ['index.htm','index.html','default.htm']
});
instance.listen(8443, '127.0.0.1');
console.log('server started.');
為了保證設定可能的變化能被處理,在測試的時候需要調整dirindex參數,讓實際存在的index檔在設定中的順序不一樣,這樣比較能確定程式的邏輯沒問題。
現在需要的參數有對應到檔案系統的路徑以及預設的index檔,為了程式的彈性,只好調整一下架構:
即使是這樣,每次測試不同設定組合時,還需要改這支程式,有點麻煩,解決之道是把他作成模組,這個明天再來處理。