これの続きなのでタイトルを引き継ぐが、中身は既に「http.server@python3 相当」の範疇を超えてる。ejs を使うなら例えば jinja2 相当、express を使うなら例えば django 相当。
まずは「すんげー WEB フレームワーク」には頼らないまま、「すんげー Templating」だけに頼るモード。「すんげー WEB フレームワーク」が独自のテンプレートエンジンを作ってる場合もあるとは思うが、いくつかみた中で ejs 依存のものと Pug 依存のものがあることは見つけた。どれがメジャーで無難なのはまだよくわからんけど、とりあえず「メジャーな気がする Express」で使える ejs か、あるいは置かれてる場所から mozilla 謹製っぽい nunjucks あたりがいいのかしらね? とりあえず ejs から始めてみるか。
とりあえず拡張子に応じて、なんてことをせずに「問答無用で ejs 経由」(データは渡さない一番シンプルなやつ):
1 var http = require('http');
2 var url = require('url');
3 var fs = require('fs');
4 var listenport = parseInt(process.argv[2], 10) || 8080;
5 var mime = require('mime-types');
6 var ejs = require('ejs');
7
8 function handler(req, res) {
9 var q = url.parse(req.url, true);
10 var filename = "." + q.pathname;
11 var conttype = mime.lookup(q.pathname) || 'text/plain';
12 fs.readFile(filename, function(err, data) {
13 if (err) {
14 res.writeHead(404, {'Content-Type': 'text/html'});
15 return res.end("404 Not Found");
16 }
17 res.writeHead(200, {'Content-Type': conttype});
18 let cont = ejs.render(data.toString());
19 res.write(cont);
20 return res.end();
21 });
22 }
23
24 http.createServer(handler).listen(listenport);
データは渡さなくてもかなりのことが出来る(サーバサイド node.js で出来ること全て書けるので)が、ひとまずすんげーアホな例:
1 <html>
2 <body>
3 <ul>
4 <% for (let i = 0; i < 5; i++) {%>
5 <li>hoge<%= i %></li>
6 <% } %>
7 </ul>
8 </body>
9 </html>
たとえばこれを zzz.html という名前で filesserv.js と同じフォルダに置いたとして、ポート 8081 でサービスするとして:
(今の場合の)render の第二引数・第三引数に何かを渡せば普通の WEB アプリケーションになっていくが、最低でも「組み込み関数」に相当するものや「ベーステンプレート置き場のパス」などを渡すことになるであろう、みたいな流れはさ、いつものヤツだよね。ざっとドキュメントを眺めた感じだと、Go 言語組み込みので苦労したような妙な制約はなさそう。
ちょっと不自然な例になるのは覚悟で、ひとつ前のネタで予告した通り「node.js だけで作るのではなく、自由に好きなものを使っちゃるぜ」に道筋をつけるために、「render に渡すべきデータを外部の python で作る」という妙な実例にしてみる:
1 var http = require('http');
2 var url = require('url');
3 var fs = require('fs');
4 var listenport = parseInt(process.argv[2], 10) || 8080;
5 var mime = require('mime-types');
6 var ejs = require('ejs');
7 /* 新しい node.js の場合は require('child_process') */
8 const subprocess = require('child_process');
9
10 function handler(req, res) {
11 var q = url.parse(req.url, true);
12 var filename = "." + q.pathname;
13 var conttype = mime.lookup(q.pathname) || 'text/plain';
14 fs.readFile(filename, function(err, data) {
15 if (err) {
16 res.writeHead(404, {'Content-Type': 'text/html'});
17 return res.end("404 Not Found");
18 }
19 res.writeHead(200, {'Content-Type': conttype});
20 const pyres = subprocess.spawnSync("py", ["hoge.py"]);
21 let ctx = JSON.parse(pyres.stdout.toString());
22 /*console.log(ctx);*/
23 let cont = ejs.render(data.toString(), ctx);
24 res.write(cont);
25 return res.end();
26 });
27 }
28
29 http.createServer(handler).listen(listenport);
1 # -*- coding: utf-8 -*-
2 # CGI のマナーだのそう言ったことなど知ったことではない、ほんとにただのただの
3 # python スクリプトだよ、標準出力に json を吐き出すだけの。
4 import json
5
6 print(json.dumps({"user": "foo", "host": "bar"}))
1 <html>
2 <body>
3 <h1><%= host %></h1>
4 <p>Hello, <%= user %></p>
5 </body>
6 </html>
「py」は Windows にしかないランチャなので、Unix ユーザなどは適宜読み替えてほしいのだが、ここがね、「Docker に頼るなら」に関係してくるんだよね。Docker の linux コンテナを使うなら、こういう「Windows なら」みたいな但し書きがいらなくなるんだわ。あと require(‘child_process’) 部分もね。ステキ。まだ皮算用だけど。
続いて「すんげー WEB フレームワーク」の一つの Express + ejs。元にしたサンプルの都合でインターフェイスはちょっと変えるが、まぁ簡単だわ:
1 /*
2 * https://github.com/expressjs/express/blob/master/examples/ejs/index.js と
3 * 比較しながら読めば理解は捗る、ハズ。
4 */
5 var listenport = parseInt(process.argv[2], 10) || 8080;
6 var express = require('express');
7 var path = require('path');
8 var app = module.exports = express();
9 /* 新しい node.js の場合は require('child_process') */
10 const subprocess = require('child_process');
11
12 app.engine('.html', require('ejs').__express);
13
14 // 「Optional since express defaults to CWD/views」
15 // まぁ「とっぱじめ」としてカレントディレクトリを使うんでもいいんだけど、
16 // 「お行儀」と見做して真似しとく。
17 app.set('views', path.join(__dirname, 'views'));
18
19 // examples/ejs/index.js の真似から始めたいので、/any.html みたいなのに
20 // 応答する例ではなくて、エントリポイントは / だけ、というものにしとく。
21 app.get("/", (req, res) => {
22 const pyres = subprocess.spawnSync("py", ["hoge.py"]);
23 let ctx = JSON.parse(pyres.stdout.toString());
24 // ここで「zzz3.html」ではなく「zzz3」と書くための方法は元の
25 // examples/ejs/index.js にある。
26 res.render("zzz2.html", ctx);
27 });
28
29 /* istanbul ignore next */
30 if (!module.parent) {
31 app.listen(listenport);
32 console.log('Express started on port ' + listenport);
33 }
hoge.py も zzz2.html も同じものだが、zzz2.html は views/ フォルダに移動する。結果は同じである:
同じというか、/ にしか反応しないので同じではないけれど、ルーティングが柔軟に出来ることは既にドキュメントで確認済み。まぁこれならやりたいことはすぐに出来るであろう。