Node-REDでハマりがちな罠をまとめます。
1. Debugの内容が途切れる
人にNode-REDを薦めると最初に言われるのがコレ。
ほとんどの場合、プロパティを絞り込んでデバッグするのでデフォルトの文字数で問題ないのですが、用途によってはどうしてもメッセージ全体を確認しないといけないような場合もあります。
その場合はDebugに表示できる文字数を増やしましょう。Debugに表示できる文字数はsettings.js
のdebugMaxLength
の値を増やすだけです。
2. データ型
Debugで処理対象のデータがJSONに見えたとしても、それはObjectでなくString(文字列)かもしれません。
以下は、あるMQTTブローカをSubscribeしているflowですがDebugをよく見るとStringと表示されてます。
これをこのままswitch nodeで条件分岐しようとすると、以下のような「プロパティがない」というエラーが表示されます。
ここでは条件の対象としてpayload.d.id
というプロパティを指定していますが、対象データがStringのままなので何を指定してもダメです。まずは対象のデータ型をObjectへ変換します。
JSON StringをObjectに変換するにはfunction nodeでJSON.parse()
しても良いのですが同様のことはJSON nodeで可能です。
以下のようにするとMQTTブローカから取得したJSON Stringを一旦JSON Obejctへ変換してから条件分岐へ流すようにできます。
3. HTTPレスポンス
http in nodeでNode-REDにEndpointを作成してWebサーバとして処理を待ち受けるというようなユースケースはよくあります。
たとえば以下のように[get]/red/test
というEndpointを作成してアクセスが来たらtemplate nodeに指定したWebページを表示します(Webページは「テスト」という文字だけ表示されるようになっています)
しかし実際に/red/test
にアクセスしてみるとタイムアウトします(Node-REDが動作している環境によって挙動は変わります)
これは、先ほどのflowのhttp in nodeでHTTPリクエストを受け付けたのにHTTPレスポンスを返す処理を設定していないため、クライアント(ブラウザ)が延々とHTTPレスポンスを待ち続けている状態になっているからです。
以下のようにhttp out nodeを設定して正しくHTTP通信を実装してください。
ちなみにhttp out nodeの位置にも気をつけましょう。flowのどこかに配置すれば一応HTTP通信が成立して動いているようには見えますが、flowで一連の処理をした後の値をクライアントへ返したいような場合は、その処理の後続としてhttp out nodeを配置するようにしましょう。
4. flowの冗長化
同じような動作をするflowを幾つも並べていませんか?以下は先ほどのWebページへアクセスするflowをNode-REDサイトだけでなくGoogleとYahooに対しても行うようにコピーしただけです。
もちろんコレでも良いのですが、アクセス先が増えたりflowが複雑になってくると破綻してきます。flowの共通部分が何かを意識して差異を動的な値として1本のflowに与えていくような設計にしてみましょう。
たとえば上記flowの場合はアクセス先以外はすべて共通なので、Node-RED, Google, YahooのURLを動的なパラメータとして渡せるようにしてみます。
http request nodeのinfoを見るとアクセスするURLはmsg.url
として動的に値を渡すことができます(他にもmethodやheaderも渡すことができると書いてます)
ということでアクセス先のURLを配列として保持して以下のようにloopさせることで1本の処理に動的なパラメータを渡して処理するflowになります。
以下をインポートするとサンプルフローを再現できます。
[{"id":"1d42eedf.e2bd11","type":"http request","name":"","method":"GET","ret":"txt","url":"","x":157,"y":150,"z":"96d4a95.f692b58","wires":[["e8e419f0.171be8"]]},{"id":"a0f49389.5f0b7","type":"inject","name":"","topic":"","payload":"","payloadType":"none","repeat":"","crontab":"","once":false,"x":145,"y":88,"z":"96d4a95.f692b58","wires":[["eb81bea6.147e4"]]},{"id":"e8e419f0.171be8","type":"html","tag":"title","ret":"html","as":"single","name":"","x":318,"y":150,"z":"96d4a95.f692b58","wires":[["963ff6aa.69c008"]]},{"id":"963ff6aa.69c008","type":"debug","name":"","active":true,"console":"false","complete":"payload","x":477,"y":150,"z":"96d4a95.f692b58","wires":[]},{"id":"6c9c4de.f9363b4","type":"function","name":"","func":"msg.url = msg.payload;\nreturn msg;","outputs":1,"x":498,"y":97,"z":"96d4a95.f692b58","wires":[["1d42eedf.e2bd11"]]},{"id":"eb81bea6.147e4","type":"function","name":"for each item","func":"if( msg.i == undefined ) msg.i = 0;\nif( msg.items == undefined ) {\n\tmsg.items = [\n\t\t\"http://nodered.org/\",\n\t\t\"http://google.com/\",\n\t\t\"http://yahoo.com/\"\n\t];\n}\n\nmsg.payload = msg.items[ msg.i ];\n\nreturn msg;","outputs":1,"x":322,"y":92,"z":"96d4a95.f692b58","wires":[["794b0123.86b5","6c9c4de.f9363b4"]]},{"id":"794b0123.86b5","type":"function","name":"++","func":"if ( (msg.i += 1) < msg.items.length ) {\n\treturn msg;\n}","outputs":1,"x":323,"y":44,"z":"96d4a95.f692b58","wires":[["eb81bea6.147e4"]]}]
もちろんアクセス先が数千とか数万箇所になってくれば、配列ではなくDBやファイルに永続化してflowからアクセス先情報を完全に分離しましょう。
また、このように後続のnodeに動的な値を渡すことがNode-RED活用のコツの一つだと思います。データベース系のnodeはSQLなどデータベース操作をするスクリプトを前段のnodeから渡すようにinfoに書かれていることがほとんどです。
5. プロパティ値の上書き
先ほどのWebアクセスのflowでhttp request nodeのURLを指定したまま外部から動的に値を渡すと以下のようなエラーが表示されます。
これはメッセージ内に表記されているリンク先( http://bit.ly/nr-override-msg-props )の通り、nodeに設定した固定の値を上書きすることで予期しない結果になる事象があるため、file系nodeやhttp系nodeでプロパティの上書きを推奨しないというものです。
特にfunction nodeで以下のように msg
を返すような書き方が散見されますが、これは msg
オブジェクトの他のプロパティを無意識に消滅させてしまいます。Node-REDのnodeは msg
オブジェクトの受け渡しを前提に動作している場合が多いので以下の書き方をしてしまうと思わぬハマりに遭遇してしまいます。
retuen msg = {
payload: "hoge"
};
6. template nodeでのプロパティアクセス
template nodeでデータをバインドしてWebページとして返したり、メールの本文として整形したりといったユースケースはよくあります。
この時のプロパティアクセスは必ず.
(ドット)で辿ります。['title']
などブラケットでは辿れません。
上記のようなObjectを流した場合はハマらないのですが、配列を流した時に慣習的に[0]
と書いてしまいがちです。
7. あるnodeの状態によって後続のflowを実行したい
例えば、以下のようにメール送信するflowがあったとします。ただ、email nodeの後続に端子がないので後続のflowを作りたくても作れません。
こういう場合は、以下のようにstatus nodeを置いてemail nodeの状態を監視します。
こうすることでメール送信してみると、以下のようにメール送信時とメール送信完了時の2回、email nodeの状態がdebug nodeに通知されますので、あとはswitch nodeなどで status.text
の内容で後続の処理を分岐するような流れになります(ステータスメッセージはnodeの作り次第になりますのでemail nodeの送信完了時のように何もメッセージがないというような場合も往々にしてあります...)
8. 日本語のプロパティにアクセスする方法
template node や change node で日本語のプロパティにアクセスする場合は以下のようにします。
template node
change node
9. template node で文字化けする
以下の画像のURLのところですが {{
だと文字参照として表示されてしまいます。
<img src="https://d1d7kfcb5oumx0.cloudfront.net/articles/images/5b30b3cee08d441789edad89/thumb_0.jpg" />
<ul>
<li>タイトル: 生後9ヶ月メス1匹</li>
<li>都道府県: 東京</li>
<li>所在地: 中野区 新中野駅 猫</li>
<li>本文: 先住の犬と相性が悪くて泣く泣く里親に出すことにしました メス白黒のパンダがいます !早急にお願いします ワクチン 去勢はしてないです</li>
</ul>
<hr />
以下の画像のように {{{
で囲むと文字参照として表示されません。
<img src="https://d1d7kfcb5oumx0.cloudfront.net/articles/images/5b30b3cee08d441789edad89/thumb_0.jpg" />
<ul>
<li>タイトル: 生後9ヶ月メス1匹</li>
<li>都道府県: 東京</li>
<li>所在地: 中野区 新中野駅 猫</li>
<li>本文: 先住の犬と相性が悪くて泣く泣く里親に出すことにしました メス白黒のパンダがいます !早急にお願いします ワクチン 去勢はしてないです</li>
</ul>
<hr />
10. 動的なキーでオブジェクトにアクセスしたい
javascriptで書くと以下のようなアクセスのことです。
let obj = {
a: "田中",
b: "山田",
}
const key = "a";
console.log(obj[key]); // => 田中
change node でも以下のように書けます。
以上、今後もハマり次第こちらに追加していきたいと思います。