Node.js 上のウェブサイトで HTML, CSS 等を読み込む

Node.js で HTML ファイルからレンダリングするのは難しいことではありませんが、Python + Flask のときと同じ感じで書いたらはまりました。

レンダリング

HTML

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Hello</title>
</head>
<body>
  <h1>Hello Node.js!</h1>
</body>
</html>

JavaScript

var http = require('http');
var fs = require('fs');

http.createServer(function(request, response) {
    fs.readFile('./index.html', function(err, data) {
        response.writeHead(200, {'Content-Type': 'text/html'});
        response.write(data);
        response.end();
    });
}
}).listen(8000);

HTML と JavaScript ファイルを用意し、Node.js がインストールされている環境で node index.js を実行すれば、この例の通りでは http://localhost:8000 でローカルサーバが建てられます。

HTML を編集しただけではファイルを読み込めない

例えば、外部スタイルシートを読み込もうと思ったとき。

<link rel="stylesheet" href="./css/style.css">

<head> 内に CSS ファイルを読み込むタグを追加すればよい、というわけではないようです。この状態で実行すると、ずっと Pending で読み込みが終わりません。

Request を拾って分岐処理

方法の一つとして、ブラウザから要求されたファイルごとに処理することができます。

var http = require('http');
var fs = require('fs');

http.createServer(function(request, response) {
    switch (request.url) {
        case '/':
            fs.readFile('./index.html', function(err, data) {
                response.writeHead(200, {'Content-Type': 'text/html'});
                response.write(data);
                response.end();
            });
            break;
        case '/css/style.css':
            fs.readFile('./css/style.css', function(err, data) {
                response.writeHead(200, {'Content-Type': 'text/css'});
                response.write(data)
                response.end();
            });
            break;
    }
}).listen(8000);

(参考) node.js html、CSS、画像ファイルなどの読み込みについて | Teratail

request.url の値を要求ファイルの拡張子などで分岐すれば、より簡潔になります。静的なファイルは Node.js ではなくウェブサーバに処理させたほうが良いそうですが。

最後に

Flask では指定されたフォルダ名を用いることでそこら辺の処理を自動化してくれているのでしょうし、Apache のドキュメントルートに置いているのも自動で処理してくれているのでしょう。エラー処理とかロギングとかも用意しないといけなさそうな感じですね。

要求に対して応答を返す処理を書くことはほぼなかったので、ある意味いい機会になったかもしれません。