node.js 超入門③簡単なwebアプリケーションを作ってみる


ついに第3回です。

[第一弾](http://qiita.com/ritukiii/items/7f28554369d63eb373c3)ではnode.jsの超基礎を学びながら簡単なwebサーバを作りました。
[第二弾](http://qiita.com/ritukiii/items/8173ff98f31c2f76b39a)では、他のファイルに分割する方法や、ルーティングやファイルシステムを使ってHTMLで表示等を行いました。

じゃあ今回は、これらを使いつつ、簡単なwebアプリケーションをnode.jsで作ってみようと思います。

## [基礎編]npmとは

- [node package manager](https://www.npmjs.com/)
- PHPで言うcomposerのようなもの
- グローバルインストール
	- `npm install -g [パッケージ名]`
- ローカルインストール
	- `npm install [パッケージ名]`


## テンプレートエンジンを使ってみる

素のHTMLファイルだと、サーバ側から動的に値を渡すなどが出来ません。
そこで登場するのがテンプレートエンジンです。
PHPだとTwigやSmartyが有名ですね。

今回は`ejs`を使っみます。

```bash
$ npm install ejs
```

実際にejsテンプレートを使うにはこんな感じにしてあげます。

```
// nodeのコアモジュールのhttpを使う
var http = require('http');
var ejs  = require('ejs');
var fs     = require('fs');
var config = require('./config');
var server = http.createServer();

// テンプレートを使うときはreadFileSyncを使う
var template = fs.readFileSync(__dirname + '/hello.ejs', 'utf-8');
var msg;

var n = 0;

server.on('request', function(req, res) {
    // リクエストがきたらnを増やす
    n++;
    var data = ejs.render(template, {
        title:   'こんにちわ',
        content: "<b>世界</b>",
        n: n
    });

    // 表示させるのはtextじゃなくて、htmlなので、text/htmlにする
    res.writeHead(200, {'Content-Type' : 'text/html'});
    res.write(data);
    res.end();
});

// サーバを待ち受け状態にする
// 第1引数: ポート番号
// 第2引数: IPアドレス
server.listen(config.port);

console.log('ポート番号: ' + config.port);
```

### ポイント

- テンプレートを使うときは、`fs.readFileSync`を使う
- ejsのrenderする時の第二引数に、渡したい変数を渡してあげる

#### ※ [注意] npmでカレントディレクトリにインストールされない場合

`npm install`をした時に、なぜかカレントディレクトリに`node_modules`ができずに、
親ディレクトリの`node_modules`にインストールされる場合があります。

詳しくは[こちらのリンク](http://qiita.com/tsuda/items/175ef29d45d155b3f405)が非常に参考になりましたので見てみて下さい。


因みに、カレントディレクトリで`npm init`しておけば、この現象を防ぐ事が可能です。


## formからデータを受け取る

まずはformを受け取れるようにejsの方を修正しておきましょう。

```
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>簡易掲示板</title>
</head>
<body>

<form action="/" method="post">
  <input type="text" name="user_name">
  <input type="submit" value="投稿">
</form>

<%# 実際に投稿された内容を表示 %>
<ul>
  <% for (var i = 0; i < posts.length; i++) { %>
    <li><%= posts[i] %></li>
  <% } %>
</ul>

</body>
</html>
```

次に、このフォームから受け取ったデータをテンプレートに渡して表示出来るようにします。

```
// nodeのコアモジュールのhttpを使う
var http = require('http');
var ejs  = require('ejs');
var qs   = require('querystring');
var fs     = require('fs');
var config = require('./config');
var server = http.createServer();

var posts = [];

// フォームを表示する
function renderForm(posts, res) {
    var data = ejs.render(template, {
        posts: posts
    });

    res.writeHead(200, {'Content-Type' : 'text/html'});
    res.write(data);
    res.end();
}

// テンプレートを使うときはreadFileSyncを使う
var template = fs.readFileSync(__dirname + '/hello.ejs', 'utf-8');

server.on('request', function(req, res) {
    if (req.method === 'POST') {
        req.data = "";
        // フォームからのデータを受信
        req.on("readable", function() {
            // read()はnullが来る場合もあるので空文字にする
            req.data += req.read() || '';
            console.log(req.data);
        });
        req.on("end", function() {
            // パースすると、formから入力された値をquery.nameのように使えるようになる
            var query = qs.parse(req.data);
            console.log(query);
            posts.push(query.user_name);
            renderForm(posts, res);
        });
    } else {
        renderForm(posts, res);
    }
});

// サーバを待ち受け状態にする
// 第1引数: ポート番号
// 第2引数: IPアドレス
server.listen(config.port);
```

こんな感じになります。
実際に使ってみるとこんな感じになります。

![スクリーンショット 2017-09-10 12.13.12.png](https://qiita-image-store.s3.amazonaws.com/0/23276/1e586a0c-138a-2063-2706-106c0720db7b.png "スクリーンショット 2017-09-10 12.13.12.png")


今回は、フォームから受け取った値を出せるようになったので、次回以降はこのデータを永続化させるためにDBも使ってみようかと思います。