Node.jsをインストールして一行BBSやmongoDB連携を試してみる

導入と簡単なページの作成を試してみたので、自分用メモも兼ねて。
OSはWindows 8.1

成果物は
https://github.com/yoruno07/node_prac
を参照。

# Node.jsのインストール
公式サイトよりインストーラーを使用してインストール
https://nodejs.org/ja/

今回は6.11.4 LTSを使用した。

インストール後、コンソールでバージョンが表示できれば完了

```
$ node --version
v6.11.4
```

# ローカルサーバーを立ててみる
- requireでNode.jsで用意されているhttpモジュールを読み込んでおく
- 引数のresを使用し返却するレスポンスを操作する

```JavaScript:server.js
var http = require('http');
var server = http.createServer();
server.on('request', function(req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.write('hello world');
    res.end();
});
server.listen(1337, '192.168.1.2');
```
```
node server.js
```
でサーバーを起動して指定したIPとポートでブラウザ上からアクセスし、正しく表示されればOK。

# 設定値を外部ファイル化
- 外部ファイルを作成し、直に指定していたIP・ポート番号を外出しする
- 外部ファイルの読み込みはrequireで行う（拡張子のjsは省略可能）

```JavaScript:setting.js
exports.port = 1367;
exports.host = '192.168.1.2'; 
```
```JavaScript:server.js
var http = require('http');
var settings = require('./settings');
var server = http.createServer();
server.on('request', function(req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.write('hello world 2nd');
    res.end();
});
server.listen(settings.port, settings.host);
```

# URLを取得してみる
- URLの取得は引数のreqを使用する
- 今回はswitch文でURLの末尾が/abcや/defの場合に画面に表示するメッセージを変えてみる

```JavaScript:server.js
var http = require('http');
var settings = require('./settings');
var server = http.createServer();
var msg;
server.on('request', function(req, res) {
    switch (req.url) {
        case '/abc':
         msg = 'this page is abc';
         break;
        case '/def':
         msg = 'this page is def';
         break;
        default:
         msg = 'wrong page';
         break;
    }
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.write(msg);
    res.end();
});
server.listen(settings.port, settings.host);
```

# HTMLを読み込んで表示してみる
- 外部にHTMLを用意し、server.js内で読み込み
- 外部ファイルの読み込みはfsモジュールを使う
- カレントディレクトリは__dirnameで取得可能
- ファイル読み込み時のエラーと読み込んだデータはそれぞれ引数で取得可能（今回はerrとdata）

```HTML:hello.html
<html>
    <h1>Hello World !!</h1>
</html>
```
```JavaScript:server.js
var http = require('http'),
    fs = require('fs');
var settings = require('./settings');
var server = http.createServer();
var msg;
server.on('request', function(req, res) {
        fs.readFile(__dirname + '/public_html/hello.html', 'utf-8', function(err, data) {
                if (err) {
                        res.writeHead(404, {'Content-Type': 'text/plain'});
                        res.write("not found!");
                        return res.end();
                }
                res.writeHead(200, {'Content-Type': 'text/html'});
                res.write(data);
                res.end();
            });
});
server.listen(settings.port, settings.host);
```
# ejsを使用し、HTMLをテンプレート化する
- ejsはテンプレートエンジンの１つ。今回はnpmを使用しインストールする
- コンソール上でnpm install ejsと打つことでインストール可能
- インストール時にnpm install -g ejsとするとグローバルインストールとなり、PC全体で使えるが今回はローカルインストールとする
- ejsも他のモジュールと同様にrequireで冒頭で読み込む
- ejsにrenderメソッドを使うことでテンプレート内の変数にそれぞれ値をはめてしてレンダリングすることができる
- 今回は読み込む度に変数nの値を1ずつ増やす処理を行い、画面上に表示するようにしている

```hello.ejs
<html>
    <h1><%= title %></h1>
    <p><%- content %></p>
    <p><%= n %> views</p>
</html>
```
```JavaScript:server.js
var http = require('http'),
    fs = require('fs'),
    ejs = require('ejs');
var settings = require('./settings');
var server = http.createServer();
var template = fs.readFileSync(__dirname + '/public_html/hello.ejs', 'utf-8');
var n = 0;
server.on('request', function(req, res) {
      if (req.url === '/favicon.ico') {
         return;
      }
    n++;
    var data = ejs.render(template, {
        title: "hello",
        content: "<strong>World!</strong>",
        n: n
    });
    res.writeHead(200, {'Content-Type': 'text/html'});
    res.write(data);
    res.end();
});
server.listen(settings.port, settings.host);
```
**※Chromeのみ、ブラウザ表示時にファビコンとページ本体で合計2回ずつ読み込み処理が走り、nが1更新で2ずつ増える現象が発生するため、URLが/favicon.icoの時はレンダリングを行わないようにしている。**

# 一行BBSページを作成してみる
- POSTしたデータを取得後整形するため、querystringモジュールを冒頭で読み込む
- readableでデータ読み込み中に行う処理、endでデータ読込後に行う処理を記述
- ただしreadableでは取得したデータの末尾にnullが付いてしまうため、今回はdataを使用
- POSTしたデータをreq.read()で取得し、posts配列にpush、レンダリング時にfor文で回して展開する

```bbs.ejs
    <form method="post">
        <input type="text" name="name">
        <input type="submit" value="Post!">
        <ul>
            <% for (var i=0; i < posts.length; i++) { %>
            <li><%= posts[i] %></li>
            <% } %>
        </ul>
    </form>
```
```JavaScript:server.js
var http = require('http'),
    fs = require('fs'),
    ejs = require('ejs'),
    qs = require('querystring');
var settings = require('./settings');
var server = http.createServer();
var template = fs.readFileSync(__dirname + '/public_html/bbs.ejs', 'utf-8');
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();
}
server.on('request', function(req, res) {
    if (req.method === "POST") {
        req.data = "";
        req.on("data", function(chunk) {
            req.data += chunk;
        });
        req.on("end", function(){
            var query = qs.parse(req.data);
            posts.push(query.name);
            renderForm(posts,res);
        });
    } else {
        renderForm(posts, res);
    }
});
server.listen(settings.port, settings.host);
```
# mongoDBとの連携を試してみる
npm install mongodbでmongoDBのモジュールをインストールしようとしたものの、上手くコマンドが通らず使うことができなかったので、結局mongoDBのサイトから直接インストーラーを入手してインストールした。
https://www.mongodb.com/download-center#community
（OSXの場合はHomebrewを使う方法が主流？）

インストール後データを入れるフォルダを指定する必要があるが、mongoDBのコマンド起動時に指定する方法も合ったので今回はそちらを採用。
プロジェクトのディレクトリにdataフォルダを作成し、そこを起動時に指定する。

```
mongod --dbpath ./data
```
mongoコマンドを打つ時は管理者権限で起動したコンソールでないとコマンドが通らず上手く行かなかった。
（Windowsのみ？）

- settingファイルにDB名を指定（今回はnodedb）
- mongoDBへのアクセスのために、冒頭でmongodbモジュールを読み込む
- collection名はusersを使用
- 多くのデータを扱う時はメモリの観点からstreamメソッドを使用する
- Gitで管理する時はmongoDBのデータファイルをリポジトリに含まないようにignoreしてあげる（忘れて最初そのままコミットしようとしてエラーになりました）

```.gitignore
/data/
```
```JavaScript:setting.js
exports.port = 1367;
exports.host = '192.168.1.2';
exports.db = 'nodedb';
```
```JavaScript:mongo.js
var MongoClient = require('mongodb').MongoClient,
settings = require('./settings');
MongoClient.connect("mongodb://"+settings.host+"/"+settings.db, function(err, db) {
    if (err) { return console.dir(err); }
    db.collection("users", function(err, collection) {
        var stream = collection.find().stream();
        stream.on("data", function(item) {
            console.log(item);
        });
        stream.on("end", function() {
            console.log("finished.");
        });
    });
});
```

# 参考サイト
https://dotinstall.com/lessons/basic_nodejs