node-redで簡易掲示板を作る

# 1. はじめに

node-redで、簡単な掲示板を作ってみます。厳密には、node-redというよりもhtmlのテクニックの方がメインです。

# 2. フロー全体
利用するノードはたったこれだけです。初期化が2つで、httpに関係するノードは全部で5つです。

![bbsOnNodeRed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/421f0a0f-c7d1-63ee-7dcc-9b608818d684.png)


最終的なフローはこちらです。試して見たい方は、コピペしてみてください。

<details><summary>フロー.json</summary><div>


```json
[
    {
        "id": "a5f3251e.9214f",
        "type": "http in",
        "z": "65f2617e.1211a8",
        "name": "",
        "url": "/bbs",
        "method": "post",
        "upload": false,
        "swaggerDoc": "",
        "x": 140,
        "y": 140,
        "wires": [
            [
                "2bb7d440.d44c3c",
                "794c41ad.05fbd8"
            ]
        ]
    },
    {
        "id": "4b2d6b44.44dba4",
        "type": "http in",
        "z": "65f2617e.1211a8",
        "name": "",
        "url": "/bbs",
        "method": "get",
        "upload": false,
        "swaggerDoc": "",
        "x": 140,
        "y": 100,
        "wires": [
            [
                "ac35ae63.4b0348"
            ]
        ]
    },
    {
        "id": "a2781f59.0baae8",
        "type": "http response",
        "z": "65f2617e.1211a8",
        "name": "",
        "statusCode": "",
        "headers": {},
        "x": 650,
        "y": 100,
        "wires": []
    },
    {
        "id": "794c41ad.05fbd8",
        "type": "debug",
        "z": "65f2617e.1211a8",
        "name": "",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 410,
        "y": 200,
        "wires": []
    },
    {
        "id": "43e7eb10.6d8044",
        "type": "function",
        "z": "65f2617e.1211a8",
        "name": "set global",
        "func": "messlogs='';\nglobal.set('messlogs',messlogs);\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 300,
        "y": 40,
        "wires": [
            []
        ]
    },
    {
        "id": "179520ef.4f6a8f",
        "type": "inject",
        "z": "65f2617e.1211a8",
        "name": "init",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 150,
        "y": 40,
        "wires": [
            [
                "43e7eb10.6d8044"
            ]
        ]
    },
    {
        "id": "ac35ae63.4b0348",
        "type": "function",
        "z": "65f2617e.1211a8",
        "name": "make a response",
        "func": "msgBody01=\"<form action=\\\"bbs\\\" method=\\\"post\\\"><input type=\\\"textarea\\\" name=\\\"myarg01\\\" value=\\\"タイトル\\\"><BR><textarea  rows=\\\"10\\\" cols=\\\"50\\\" name=\\\"myarg02\\\" >メッセージ</textarea><BR><input type=\\\"submit\\\" value=\\\"送信\\\"></form>\"\n\nmesslogs = global.get('messlogs');\n\nmsg.payload = msgBody01 + messlogs;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 490,
        "y": 100,
        "wires": [
            [
                "a2781f59.0baae8",
                "794c41ad.05fbd8"
            ]
        ]
    },
    {
        "id": "2bb7d440.d44c3c",
        "type": "function",
        "z": "65f2617e.1211a8",
        "name": "get posted",
        "func": "var mydate = new Date();\n\nmesslogs = '';\nmesslogs += 'datetime:\\n';\nmesslogs += mydate.getFullYear();\n// ref to http://www.digitra.net/2016/02/javascript.html\nmesslogs += (\"0\"+(mydate.getMonth() + 1)).slice(-2);\nmesslogs += (\"0\"+mydate.getDate() ).slice(-2);\nmesslogs += '_';\nmesslogs += (\"0\"+mydate.getHours()).slice(-2);\nmesslogs += (\"0\"+mydate.getMinutes()).slice(-2);\nmesslogs += (\"0\"+mydate.getSeconds()).slice(-2);\nmesslogs += '<BR>\\n';\nmesslogs += 'title:\\n';\nmesslogs += msg.req.body.myarg01;\nmesslogs += '<BR>\\n';\nmesslogs += 'messege:<BR>\\n';\nmesslogs += msg.req.body.myarg02;\nmesslogs += '<BR>\\n';\nmesslogs += '<BR>\\n';\nmesslogs +=global.get('messlogs');\nglobal.set('messlogs',messlogs);\n//msg.messlogs=messlogs;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 310,
        "y": 140,
        "wires": [
            [
                "ac35ae63.4b0348"
            ]
        ]
    }
]
```

</div></details>

# 3. 解説

## 3.1. 初期化部分

起動時にglobal変数を定義するために、初回だけ起動するinjectionノードを設置します。必要な設定は、下にあるチェックを入れるだけです。

![node01_init.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/cdea6277-d24e-4a70-49ed-c79306426578.png)


その後ろに、functionノードを配置し、接続します。
コードの紙はたったこれだけです。messlogsという変数をglobalとして定義し、他のfunctionノードでも使えるようにしておきます。

```javascript
messlogs='';
global.set('messlogs',messlogs);

return msg;
```

## 3.2. http get部分

ブラウザでURLを叩いた際に、応答する処理部分です。
まずは、http-inノードを設置します。この設定では、メソッドを```get```に、URLは、今回は```/bbs```にします。
![node02_httpget.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/22e266ea-dfd3-bcb0-fea7-12ea8141a9b6.png)



この後ろに、ブラウザに表示するためのhtml記述のページを作ります。
msgBody01が、入力フォーム部分です。
messlogsは、グローバル変数に入っている文字列を格納するローカル変数です。
msgBody01の後ろにmesslogsを挿入して、payloadに入れます。

msgBody01の内容が、post側のフローと一致しないと動きません。
post側へのURLは、```bbs```にしてます。よって、post処理側のhttp-inノードにもURLが```bbs```にする必要があります。（getと同じURLにしていますが、別の名前にしてもＯＫです。その場合は、post側もそれに変更してください）
タイトルを入れるフォームの```name```を```myarg01```に、メッセージの```name```を```myarg02```にしていますので、これもpost側と一致させておく必要があります。


```javascript
msgBody01="<form action=\"bbs\" method=\"post\"><input type=\"textarea\" name=\"myarg01\" value=\"タイトル\"><BR><textarea  rows=\"10\" cols=\"50\" name=\"myarg02\" >メッセージ</textarea><BR><input type=\"submit\" value=\"送信\"></form>"

messlogs = global.get('messlogs');

msg.payload = msgBody01 + messlogs;
return msg;
```

## 3.3. http post部分

http post側は、webブラウザに表示されているページの送信ボタンを押した際に、接続される処理部分です。

メソッドは、```post```にして、ファイルのアップロードは無しにし、URLは、web用htmlで記載したものと同じ```/bbs```にします。


![node03_httppost.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/ff9fe6df-9384-b71f-1b42-0684e34d2540.png)


その後ろに、postで受信した情報を展開するために、functionノードを設置します。
行う処理は、日時、タイトル、メッセージの抽出と変数格納を行い、その後ろに、過去の記録を追加し、最後にglobal変数に保存します。（変数名がローカルとグローバルとで、ややこしいと思う場合は、ローカルを別の変数名にすると判り易くなると思います）


postで受信した値は、```msg.req.body.NAME```に格納されます。NAMEは、web用htmlで記載したものと同じ ```myarg01```と```myarg02```になります。


```javascript
var mydate = new Date();

messlogs = '';
messlogs += 'datetime:\n';
messlogs += mydate.getFullYear();
messlogs += ("0"+(mydate.getMonth() + 1)).slice(-2);
messlogs += ("0"+mydate.getDate() ).slice(-2);
messlogs += '_';
messlogs += ("0"+mydate.getHours()).slice(-2);
messlogs += ("0"+mydate.getMinutes()).slice(-2);
messlogs += ("0"+mydate.getSeconds()).slice(-2);
messlogs += '<BR>\n';
messlogs += 'title:\n';
messlogs += msg.req.body.myarg01;
messlogs += '<BR>\n';
messlogs += 'messege:<BR>\n';
messlogs += msg.req.body.myarg02;
messlogs += '<BR>\n';
messlogs += '<BR>\n';
messlogs +=global.get('messlogs');
global.set('messlogs',messlogs);
return msg;
```

日付けは、Date()で取得した現在日時ですが、そのオブジェクトのままですので、年月日時刻をそれぞれ抽出します。注意点としては、月は0始まりなので1を加えます。また、1桁の時と2桁の時とで文字の長さが異なるので、常に2桁にする処理を加えました。

2桁にする処理は下記を参考にしました。（「javascript date 0埋め」で検索するとヒットします）

http://www.digitra.net/2016/02/javascript.html



# 4. 動作確認

node-redの動いているサーバ（ラズパイなど）のIPアドレスとポート番号の後ろに設定した/bbs をつけるとアクセスできます。
（例： http:192.168.1.2:1880/bbs ）
タイトルとメッセージを記載し、送信ボタンを押すと、下にその内容が記録されます。なお、ファイルには保存していませんので、再起動したりデプロイし直すと内容は消えます。

![pageBbs.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/89f18bcb-9fd0-889b-2078-84a6e260cf40.png)


# 5. おわりに

居室で記載したメモを別の部屋で見たい時、LAN内にBBSみたいなのがあると良いと思い、作ってみました。例えば、IoTでnode-redを稼働させているラズパイはあるけど、新たにwebサーバを立ち上げるのは面倒という時に重宝するように思います。

今回、メッセージ部に改行があっても表示には反映されないようになっていますが、変換処理をfunctionノードに追加するのもありかもしれません。