Tommy
今天要用Node.js打造一個靜態網頁伺服器
只單純提供html, css, js, jpg......等等這類靜態檔案給瀏覽器
今天終於要派上用場了!!!
因為真的很冷啊
1. 很冷 => 要讓伺服器硬體降溫
2. 很吵 => 機器運轉風扇聲音很大
3. 很多綠色乖乖 => 一種有拜有保佑的概念
4. 24H運作 => 不然就不能半夜逛網站了
不要不信邪啊
Node.js本身自帶HTTP模組
可以自己啟動一個伺服器
四大重點:Request, Response, IP, Port
指向自己電腦的IP位置
localhost也是指向自己電腦的IP
剛入門後端時貼127.0.0.1的網址給別人
若不是監聽在預設port就必須自帶port編號
例如:http://192.168.0.1:3000
var http = require('http');
var server = http.createServer(function(request, response) {
response.write('hello world');
response.end();
});
server.listen(3000, '127.0.0.1');
C:\>type demo.js
console.log('Hello World');
C:\>node demo
Hello World
C:\>_
(回到C:\>游標)
因為若是關閉了,瀏覽器就無法和伺服器連線
所以必須要一直保持監聽狀態,等待瀏覽器連線
C:\>node web.js
(不會跳回C:\>游標)
瀏覽器不知道什麼時候結束
C:\>node web
events.js:183
throw er; // Unhandled 'error' event
^
Error: listen EADDRINUSE :::3000
at Object._errnoException (util.js:1022:11)
at _exceptionWithHostPort (util.js:1044:20)
at Server.setupListenHandle [as _listen2] (net.js:1367:14)
at listenInCluster (net.js:1408:12)
at Server.listen (net.js:1492:7)
at Object.<anonymous> (C:\Users\Tommy\Desktop\demo\web.js:6:8)
at Module._compile (module.js:652:30)
at Object.Module._extensions..js (module.js:663:10)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
告訴瀏覽器傳送什麼內容
var http = require('http');
var server = http.createServer(function(request, response) {
response.writeHead(200, {
'Content-Type': 'text/html'
})
response.write('<h1>Hello World</h1>');
response.end();
});
server.listen(3000);
但有給Content-Type瀏覽器就會用這個類型解讀
試試看text/plain和image/png和application/zip
任何回應都要有一個status code
沒問題就給200 OK
var fs = require('fs');
var http = require('http');
var server = http.createServer(function(request, response) {
response.writeHead(200, {
'Content-Type': 'text/html'
})
var html = fs.readFileSync('./index.html')
response.write(html);
response.end();
});
server.listen(3000);
當大量request進來才不會被同步操作卡住
var fs = require('fs');
var http = require('http');
var server = http.createServer(function(request, response) {
response.writeHead(200, {
'Content-Type': 'text/html'
})
fs.readFile('./index.html', function(err, html) {
if (!err) {
response.write(html);
response.end();
}
});
});
server.listen(3000);
http://127.0.0.1:3000/index.css
http://127.0.0.1:3000/index.js
看到都是index.html見鬼了
/filename.html
/path/filename.txt
var fs = require('fs');
var http = require('http');
var server = http.createServer(function(request, response) {
console.log(request.url)
response.writeHead(200, {
'Content-Type': 'text/html'
})
fs.readFile('./index.html', function(err, html) {
if (!err) {
response.write(html);
response.end();
}
});
});
server.listen(3000);
會包含GET參數
http://127.0.0.1:3000/index.css?v=1
var url = require('url');
var fs = require('fs');
var http = require('http');
var server = http.createServer(function(request, response) {
var pathname = url.parse(request.url).pathname;
console.log(pathname);
response.writeHead(200, {
'Content-Type': 'text/html'
})
fs.readFile('./index.html', function(err, html) {
if (!err) {
response.write(html);
response.end();
}
});
});
server.listen(3000);
var url = require('url');
var fs = require('fs');
var http = require('http');
var server = http.createServer(function(request, response) {
var pathname = url.parse(request.url).pathname;
console.log(pathname);
response.writeHead(200, {
'Content-Type': 'text/html'
})
fs.stat('.' + pathname, function(err, stats) {
console.log(err, stats)
})
fs.readFile('./index.html', function(err, html) {
if (!err) {
response.write(html);
response.end();
}
});
});
server.listen(3000);
也有可能是資料夾而不是檔案
檢查是不是檔案
var url = require('url');
var fs = require('fs');
var http = require('http');
var server = http.createServer(function(request, response) {
var pathname = url.parse(request.url).pathname;
fs.stat('.' + pathname, function(err, stats) {
if(!err && stats.isFile()) {
fs.readFile('.' + pathname, function(err, html) {
if (!err) {
response.writeHead(200, {
'Content-Type': 'text/html'
})
response.write(html);
response.end();
}
});
} else {
response.writeHead(404);
response.write('Not Found');
response.end();
}
})
});
server.listen(3000);
目前所有的Content-Type都固定是text/html
記得要先npm install mime
mime.getType(filename)
var mime = require('mime');
var url = require('url');
var fs = require('fs');
var http = require('http');
var server = http.createServer(function(request, response) {
var pathname = url.parse(request.url).pathname;
fs.stat('.' + pathname, function(err, stats) {
if(!err && stats.isFile()) {
fs.readFile('.' + pathname, function(err, html) {
if (!err) {
response.writeHead(200, {
'Content-Type': mime.getType(pathname)
})
response.write(html);
response.end();
}
});
} else {
response.writeHead(404);
response.write('Not Found');
response.end();
}
})
});
server.listen(3000);
http://127.0.0.1:3000/
var mime = require('mime');
var url = require('url');
var fs = require('fs');
var http = require('http');
var server = http.createServer(function(request, response) {
var pathname = url.parse(request.url).pathname;
if (pathname.endsWith('/')) {
pathname += '/index.html';
}
fs.stat('.' + pathname, function(err, stats) {
if(!err && stats.isFile()) {
fs.readFile('.' + pathname, function(err, html) {
if (!err) {
response.writeHead(200, {
'Content-Type': mime.getType(pathname)
})
response.write(html);
response.end();
}
});
} else {
response.writeHead(404);
response.write('Not Found');
response.end();
}
})
});
server.listen(3000);
這兩個網址不一樣嗎?
一般來說檔案至少會有個.
沒有.那我就可能是資料夾
※當然有例外(我們暫不考慮)
// ...(略)
} else {
if (!pathname.includes('.')) {
response.writeHead(302, {
'Location': pathname + '/' + (url.parse(request.url).search || "")
})
response.end();
return;
}
}
fs.stat('.' + pathname, function(err, stats) {
// ...(略)
// 註解剛剛前面寫的部分
// ...(略)
fs.stat('.' + pathname, function(err, stats) {
if(!err && stats.isDirectory()) {
response.writeHead(302, {
'Location': pathname + '/' + (url.parse(request.url).search || "")
})
response.end();
return;
}
if(!err && stats.isFile()) {
// ...(略)
url傳送中文都會被編碼起來
有何不同?
//...(略)
}
var relativePathname = decodeURIComponent(pathname);
fs.stat('.' + relativePathname, function(err, stats) {
if(!err && stats.isDirectory()) {
response.writeHead(302, {
'Location': pathname + '/' + (url.parse(request.url).search || "")
})
response.end();
return;
}
if(!err && stats.isFile()) {
fs.readFile('.' + relativePathname, function(err, html) {
if (!err) {
response.writeHead(200, {
'Content-Type': mime.getType(relativePathname)
})
//...(略)
透過啟動參數決定從哪個位置當作網站起點
// ...(略)
}
var relativePathname = decodeURIComponent((process.argv[2] || ".") + pathname);
fs.stat(relativePathname, function(err, stats) {
if(!err && stats.isDirectory()) {
response.writeHead(302, {
'Location': pathname + '/' + (url.parse(request.url).search || "")
})
response.end();
return;
}
if(!err && stats.isFile()) {
fs.readFile(relativePathname, function(err, html) {
if (!err) {
// ...(略)
節省網路頻寬
雖然現在聽說sha1已經被破解了
但我們拿來做快取的檢查OK的
記得要先require喔
// ...(略)
if (!err) {
var hash = crypto.createHash('sha1').update(html).digest('base64');
if (request.headers['if-none-match'] == hash) {
response.writeHead(304);
response.end();
return;
}
response.writeHead(200, {
'Content-Type': mime.getType(pathname),
"Etag": hash
})
// ...(略)
// ...(略)
}
var headers = {
'Content-Type': mime.getType(pathname),
"Etag": hash
};
if (headers["Content-Type"].startsWith('image/')) {
delete headers.Etag;
headers["Cache-Control"] = "max-age=3600";
}
response.writeHead(200, headers);
response.write(html);
// ...(略)
打造出一個可客製化的靜態網頁伺服器
var mime = require('mime');
var url = require('url');
var fs = require('fs');
var http = require('http');
var crypto = require('crypto');
var server = http.createServer(function(request, response) {
var pathname = url.parse(request.url).pathname;
if (pathname.endsWith('/')) {
pathname += 'index.html';
}
var relativePathname = decodeURIComponent((process.argv[2] || ".") + pathname);
fs.stat(relativePathname, function(err, stats) {
if(!err && stats.isDirectory()) {
response.writeHead(302, {
'Location': pathname + '/' + (url.parse(request.url).search || "")
})
response.end();
return;
}
if(!err && stats.isFile()) {
fs.readFile(relativePathname, function(err, html) {
if (!err) {
var hash = crypto.createHash('sha1').update(html).digest('base64');
if (request.headers['if-none-match'] == hash) {
response.writeHead(304);
response.end();
return;
}
var headers = {
'Content-Type': mime.getType(pathname),
"Etag": hash
};
if (headers["Content-Type"].startsWith('image/')) {
delete headers.Etag;
headers["Cache-Control"] = "max-age=3600";
}
response.writeHead(200, headers);
response.write(html);
response.end();
}
});
} else {
response.writeHead(404);
response.write('Not Found');
response.end();
}
})
});
server.listen(3000);
需要的功能才寫進去,不需要的功能就不用寫進去
依照需求客製化更輕量化
當你在用Apache和IIS有想過會丟出什麼header和status code嗎?
適合完全沒使用過版本控制的人