Node.jsの勉強でちょっとつまづいたのでメモ。
課題:Node.jsでCSS、画像、JSなどが読み込まれない
Node.jsでウェブサーバーを立ててウェブサイトを表示したいが、HTMLだけ表示されてCSSや画像ファイルが読み込まれない。
結論
CSSやJS、画像(JPG、SVG、PNG等)を読み込む実装をする必要がある。
背景
作ったウェブサイトをテスト的に表示させたかった。まぁ普通にChromeを使えばいいのだが、JavaScriptやNode.jsを勉強中ということもあり、「Node.jsでウェブサーバー立ててみっかー」の勢いで試してみたらつまづいた。Express等のフレームワークを使うほどの規模ではないと判断し直書きで実装することに。 ※Expressフレームワークを使えばデフォルトでcssなど静的ファイルを読み込める
方法
こちらの記事を参考にさせていただいた。感謝。
Node.jsバージョン
v10.14.2
実装コード
index.js
ファイルを作成し、以下のコードを記述。
var http = require('http'); var fs = require('fs'); function getType(_url) { var types = { '.html': 'text/html', '.css': 'text/css', '.js': 'text/javascript', '.png': 'image/png', '.gif': 'image/gif', '.svg': 'image/svg+xml' } for (var key in types) { if (_url.endsWith(key)) { return types[key]; } } return 'text/plain'; } var server = http.createServer((req, res) => { var url = 'public' + (req.url.endsWith('/') ? req.url + 'index.html' : req.url); if (fs.existsSync(url)) { fs.readFile(url, (err, data) => { if (!err) { res.writeHead(200, {'Content-Type': getType(url)}); res.end(data); } else { res.statusCode = 500; res.end(); } }); } else { res.statusCode = 404; res.end(); } }); var port = 8000; server.listen(port, () => { console.log(`Server listening on ${port}`); });
HTML、CSS、jpg、png、svgファイルなどは適当に用意し、public
ディレクトリを作ってぶちこんでおく。
ディレクトリ構成はこんな感じ。
- index.js - public - index.html - css - img
何をしているか
http、fsモジュールを読み込む。なお練習なので変数定義もconstなどではなくvarで。
var http = require('http'); var fs = require('fs');
.html
や.css
などの拡張子で終わる場合に返すコンテンツタイプをそれぞれ定義。
function getType(_url) { var types = { '.html': 'text/html', '.css': 'text/css', '.js': 'text/javascript', '.png': 'image/png', '.gif': 'image/gif', '.svg': 'image/svg+xml' } for (var key in types) { if (_url.endsWith(key)) { return types[key]; } } return 'text/plain'; }
for...in命令
を使用し、キーの形式でパスが終了していたら、そのキーに定義されている値をコンテンツタイプとして返す。たとえば、上記の配列を使用し、レプルを立ち上げて以下のような処理を記述すると、動きがイメージしやすくなる。
for (var key in types) { console.log(`${key} = ${types[key]}`)}
これを実行すると以下の処理が返ってくる。
.html = text/html .css = text/css .js = text/javascript .png = image/png .gif = image/gif .svg = image/svg+xml
次に、サーバーが起動したときの設定。
var server = http.createServer((req, res) => { var url = 'public' + (req.url.endsWith('/') ? req.url + 'index.html' : req.url); if (fs.existsSync(url)) { fs.readFile(url, (err, data) => { if (!err) { res.writeHead(200, {'Content-Type': getType(url)}); res.end(data); } else { res.statusCode = 500; res.end(); } }); } else { res.statusCode = 404; res.end(); } });
ひとつずつ見ていく。
var url = 'public' + (req.url.endsWith('/') ? req.url + 'index.html' : req.url);
これは、リクエストしたURLの末尾が/
で終わっていたら、req.url
の末尾にindex.html
を追記して表示する設定。つまり、本当はhttp://localhost:8000/
にアクセスしようとしてるんだけど、/
で終わっているのでhttp://localhost:8000/index.html
が返ってくる。Expressとか使ってたらデフォルトになってる設定。
if (fs.existsSync(url)) { fs.readFile(url, (err, data) => { if (!err) { res.writeHead(200, {'Content-Type': getType(url)}); res.end(data); } else { res.statusCode = 500; res.end(); } });
リクエストしたファイルパスの形式に応じて、上記で定義したそれぞれのコンテンツタイプとステータスコード200を返す。res.end(data)
をして終了。それ以外はとりあえず500番を返しておく。
ステータスコード500:Internal Server Error、内部サーバエラー(ソースコードの不具合とか)
ポート番号はNode.jsデフォルトだと3000のはずだが、私の環境では8000に設定。
サーバーが起動したらコンソールにServer listening on {ポート番号}
と表示される。
var port = 8000; server.listen(port, () => { console.log(`Server listening on ${port}`);
すべて実装できたら、node index.js
でサーバーを起動してみる。
ウェブサイトが表示でき、CSSや画像ファイルも読み込まれたことが確認できた。