情報アイランド

「情報を制する者は世界を制す」をモットーに様々な情報を提供することを目指すブログです。現在はプログラミング関連情報が多めですが、投資関連情報も取り扱っていきたいです。

expressモジュールによるHTTPサーバ(15)テンプレートのレンダリング

この記事は「expressモジュールによるHTTPサーバ」という連載記事の15つ目の記事です。

その他の記事に関しては下を参照してください。

また、HTTP自体について知りたい方は下を参照してください。

この記事ではexpressモジュールを使ってリクエストに対してEJSテンプレートエンジンによるEJSテンプレートファイルのレンダリング結果の送信を行うHTTPサーバを作成する方法について説明します。

テンプレートエンジン

テンプレートエンジンを利用するにはテンプレートエンジンのモジュールが含まれるパッケージを手動でインストールしなければなりません。

EJSテンプレートエンジンの場合、npm install ejsによりインストールします。

テンプレートに関する設定項目

テンプレートに関するアプリケーションの設定項目として下の3つがあります。

  • views・・・テンプレートが保存されているフォルダのパスを設定します。複数のパスを設定する場合には配列として設定します。デフォルトはprocess.cwd() + '/views'です。
  • view cache・・・テンプレートのコンパイル結果をキャッシュするかを真偽値として設定します。デフォルトはenv設定項目がproductionである場合にはtrueであり、そうでない場合にはfalseです。
  • view engine・・・テンプレートエンジンを設定します。EJSテンプレートエンジンの場合、ejsです。この値はテンプレートエンジンのモジュールやテンプレートを読み込む際に使用されます。

なお、expressモジュールに対応している主なテンプレートエンジン(のモジュール名)には下のようなものがあります。

  • pug
  • hamljs
  • ejs
  • swig
  • whiskers

テンプレートエンジンの設定

expressモジュールはデフォルトではテンプレートファイルの拡張子に基づいてレンダリングに使用するテンプレートエンジンを決定します。

たとえば、テンプレートファイルの拡張子が.xxxである場合にはxxxという名称のモジュールがテンプレートエンジンとして読み込まれます

しかしながら、テンプレートファイルの拡張子とテンプレートエンジンのモジュール名が一致しない場合などテンプレートファイルの拡張子とテンプレートエンジンを手動で関連付けたい場合にはapp.engine関数を使用します。

var express = require('express');

var app = express();
app.engine('xxx', function (path, data, callback) {
    callback(null, 'yyy');
    //or
    callback(new Error('aaa'), null);
});

第1引数に拡張子から冒頭の.を除いたものを指定します。

第2引数にテンプレートをレンダリングする処理を関数として指定します。

この関数の第1引数はテンプレートファイルのパスであり、第2引数はデータであり、第3引数はコールバック関数です。

処理が完了した場合や処理の実行中にエラーが発生した場合には必ずこのコールバック関数を呼び出さなければなりません。

このコールバック関数の第1引数にはエラーオブジェクト(エラーが発生しなかった場合にはnull)を指定し、第2引数にはレンダリング結果(エラーが発生した場合にはnull)を指定します。

なお、第2引数の関数を指定する場合にはconsolidateモジュールが提供しているものを利用すると便利です。consolidateモジュールは主要なテンプレートエンジン毎に第2引数の関数に指定可能な関数を提供しています。

var consolidate = require('consolidate');

var dust = consolidate.dust;
var swig = consolidate.swig;
var ejs = consolidate.ejs;
var haml = consolidate.haml;
var whiskers = consolidate.whiskers;
var hogan = consolidate.hogan;
var handlebars = consolidate.handlebars;
var pug = consolidate.pug;
var mustache = consolidate.mustache;
var ect = consolidate.ect;
var dot = consolidate.dot;

レンダリング結果の送信

テンプレートエンジンによるレンダリング結果を取得し、HTTPレスポンスボディにレンダリング結果をHTMLドキュメントとして設定してレスポンスの送信を行うにはres.render関数を使用します。

app.use('xxx', function (req, res, next) {
    res.render('xxx', { yyy: 'yyy' }, function (err, result) {
        if (err) {
            res.end();
        }
    });
});

第1引数にテンプレートのパスを指定します。このパスは絶対パスかviews設定項目からの相対パスとします。このパスの拡張子を省略した場合にはview engine設定項目の冒頭に.を付加したものが拡張子となります。拡張子を省略しなかった場合にはテンプレートのレンダリングの際に拡張子に対応するテンプレートエンジンが利用されます

第2引数にデータをオブジェクトとして指定します。この引数は指定しなくても構いません。このオブジェクトのcacheプロパティの値がtrueである場合にはテンプレートのコンパイル結果がキャッシュされます。

第3引数にコールバック関数を指定します。この引数は指定しなくても構いません。この関数の第1引数はエラーオブジェクトであり、第2引数はレンダリング結果です。この引数を指定した場合にはHTTPレスポンスの送信は行われませんので注意してください。

レンダリング結果の取得

テンプレートエンジンによるレンダリング結果の取得のみを行うにはapp.render関数を使用します。

app.render('xxx', { yyy: 'yyy' }, function (err, result) {
});

この関数の引数はres.render関数のものと同じです。

サンプルコード1

クライアントからGETリクエストを受け取ったらEJSテンプレートエンジンによるEJSテンプレートファイルのレンダリング結果を返すHTTPサーバを作成し、起動します。

Enterキーが押されたらサーバを停止し、プログラムを終了します。

また、EJSテンプレートファイルは3番目のコマンドライン引数として与えられるものとします。また、テンプレートのデータはJSON形式のデータファイルにより与えられるものとし、JSON形式のデータファイルは4番目のコマンドライン引数として与えられるものとします。

express-server-ejs.js

var express = require('express');
var path = require('path');
var http = require('http');

if (process.argv.length < 4) {
    console.error('lack argument.');
    process.exit(1);
}

var app = express();
app.set('env', 'development');
app.set('x-powered-by', false);
app.set('case sensitive routing', true);
app.set('strict routing', true);
app.set('views', '.');
app.set('view cache', false);
app.set('view engine', 'ejs');
app.get('*', function (req, res) {
    res.render(process.argv[2], require(path.isAbsolute(process.argv[3]) ? process.argv[3] : path.join(process.cwd(), process.argv[3])));
});
app.get('*', function (req, res) {
    res.status(404);
    res.send(http.STATUS_CODES[404] + '\r\n');
});
app.all('*', function (req, res) {
    res.status(501);
    res.send(http.STATUS_CODES[501] + '\r\n');
});
var server = app.listen(3000, function () {
    console.log('http server is running...press enter key to exit.');

    process.stdin.on('data', function (data) {
        if (data.indexOf('\n') !== -1) {
            server.close(function () {
                console.log('http server closed.');
                process.exit(0);
            });
        }
    });
});
server.on('error', function (err) {
    console.error(err);
    process.exit(1);
});

使用パッケージ

  • express
    npm install expressでインストールします。
  • EJS
    npm install ejsでインストールします。

実行結果

実行結果の確認のためにcURLを使用しています。

現在のフォルダには下のような内容のtemplate.ejsという名称のEJSテンプレートファイルが存在しています。

<%
var n = 0;
for (var i = 0; i < vocabulary.length; i++) {
    n++;
}
-%>
<h1><%= name %></h1>
<p><%= description %></p>
<p>単語数:<%= n %></p>
<ul>
<% for (var i = 0; i < vocabulary.length; i++) { -%>
<%-    "<li>" -%>
<%=        vocabulary[i].level -%>
<%=        " " -%>
<%=        vocabulary[i].word -%>
<%=        " " -%>
<%        for (var j = 0; j < vocabulary[i].meanings.length; j++) { -%>
<%            if (j != 0) { -%>
<%=                "、 " -%>
<%            } -%>
<%=            vocabulary[i].meanings[j] -%>
<%        } -%>
<%-    "</li>" %>
<% } -%>
</ul>

また、下のような内容のvocabulary.jsonという名称のJSON形式のデータが存在しています。

{
    "name": "単語帳", 
    "description": "頑張って覚えましょう。", 
    "vocabulary": [
        {
            "word": "stale", 
            "level": "6", 
            "meanings": ["新鮮でない"]
        }, 
        {
            "word": "stylist", 
            "level": "7", 
            "meanings": ["名文家"]
        }, 
        {
            "word": "substitution", 
            "level": "8", 
            "meanings": ["代用", "代用品", "代入", "置換"]
        }, 
        {
            "word": "ventilate", 
            "level": "9", 
            "meanings": ["~を換気する"]
        }, 
        {
            "word": "ventilation", 
            "level": "10", 
            "meanings": ["換気(装置)"]
        }
    ]
}

3つ目のコマンドライン引数にtemplate.ejsを指定し、4つ目のコマンドライン引数にvocabulary.jsonを指定してコードを実行するとHTTPサーバが起動します。

C:\work\node>node express-server-ejs.js template.ejs vocabulary.json
http server is running...press enter key to exit.

cURLを使ってサーバに対してGETリクエストを行います。

C:\work\node>curl -v http://localhost:3000/
*   Trying ::1...
* Connected to localhost (::1) port 3000 (#0)
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.46.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/html; charset=utf-8
< Content-Length: 309
< ETag: W/"135-0tI32QpE/2iKe3V0zzQ9jg"
< Date: Tue, 19 Jul 2016 10:44:22 GMT
< Connection: keep-alive
<
<h1>単語帳</h1>
<p>頑張って覚えましょう。</p>
<p>単語数:5</p>
<ul>
<li>6 stale 新鮮でない</li>
<li>7 stylist 名文家</li>
<li>8 substitution 代用、 代用品、 代入、 置換</li>
<li>9 ventilate ~を換気する</li>
<li>10 ventilation 換気(装置)</li>
</ul>* Failed writing body (209 != 309)
* Closing connection 0
curl: (23) Failed writing body (209 != 309)

サーバから正しくレスポンスが返っていることが分かります。

Enterキーを押すとサーバが停止し、プログラムが終了します。

関連

pizyumi
プログラミング歴19年のベテランプログラマー。業務システム全般何でも作れます。現在はWeb系の技術を勉強中。
スポンサーリンク

-express, Node.js