Node-REDハンズオン時の質問について

以前のエントリで大学生向けのNode-REDのハンズオンをしてきたということを書いたのですが、その時のハンズオンで2件ほど質問がでました。わりとテンパっていたので、その時はあまり良い答えを提示することができませんでした。準備していなかったというのが一番大きな理由ではありますが、さすがにそのままというのも気持ちが悪いので、このエントリに綴って置こうかと思います。

uepon.hatenadiary.com

質問は以下のようなものでした。(質問文はちょっと違うかなと思いましたがこんな感じだったかと思います)

  • Node-REDを使って動的なHTMLデータを作成して表示させることができるか
  • REST APIURIを変更することはできるのか

上記のことは、できるということはわかっていましたが、実際にあまりやっていないので試してみます。

Node-REDを使って動的なHTMLデータを作成して表示させることができるか

ハンズオンではNode-REDを使用してHTMLのデータを表示するという簡単なサンプルをしていました。クエリパラメータのサンプルとして、動的な表示変更は使用していましたがHTMLはほとんど使用していなかったので、そういうふうに見えなかったのかもしれません。

今回はHTMLをちゃんと(わかりやすく)使用して動的な表示変更をしてみようと思います。

静的なHTMLデータを表示する

使用するノードは以下のノードになります。

http inノード f:id:ueponx:20210815235327p:plain

templateノード f:id:ueponx:20210815235335p:plain

http responseノード f:id:ueponx:20210815235330p:plain

http inノードを使用して、Webアクセスを受信し、templateノードでHTMLデータを生成して、http responseノードでそのHTMLデータを返送するというものになります。

これをフロー上に並べると下記の様になります。3つのノードをそのまま並べてつないだという簡単なものになります。

f:id:ueponx:20210815235338p:plain

では、それぞれのノードのプロパティを確認していきます。

http inノードのプロパティ

プロパティで変更しなければいけないのはメソッドとURLとなります。 今回のサンプルではWebブラウザでのアクセスを想定しているので、GETメソッドを設定しています。また、URLはエンドポイントとなる部分なので今回は/helloworldとしています。(アクセス時はブラウザに対してNode-REDを起動しているサーバのURLのあとにこのURLパラメータをつける形でアクセスURLを表記することになります)

f:id:ueponx:20210822192222p:plain

templateノードのプロパティ

templateノードでは、リクエストを送信したユーザに返送するHTMLデータを作成します。 テンプレートの入力ボックスにHTML形式のテキストを入力していきます。静的なデータを表示させるのであれば通常のHTMLファイル形式で大丈夫です。

f:id:ueponx:20210822192347p:plain

http responseノードのプロパティ

こちらはデフォルトのままで大丈夫です。

実行結果

こちらのフローを実行し、ブラウザでアクセスを行うと、アクセスしたブラウザが以下のように表示されます。今回は静的なHTMLテキストが返送されますので、そんなに難しくはありません。

f:id:ueponx:20210815235348p:plain

ブラウザのデベロッパーツールを使用すると、先程templateノードで入力しておいたHTML形式のデータが受信され、表示されていることがわかります。

f:id:ueponx:20210815235351p:plain

動的な表示データをHTMLとして表示する

先程は、静的なデータになっていましたが、これを改造して動的なデータに変更してみようと思います。

今回はHTTPのリクエストを得たら、外部のREST APIへアクセスを行い、その結果をレスポンスとしてHTMLの形式で送り返すというものにします。

今回使用しているのは以下のREST APIとなります。

【参考】 project.iw3.org

郵便番号から住所情報を検索、および住所情報から郵便番号を検索を行えるREST API

追加するノードはhttp requestノードとなります。

f:id:ueponx:20210815235332p:plain

こちらは、フロー上からHTTPのリクエストを送信できるので、HTMLの取得やREST APIへのアクセスなどをするために使用する事が多いノードになります。

f:id:ueponx:20210822193145p:plain

こちらをフローの2番目に追加して、その内容を続くtemplateノードに格納してHTML化してレスポンスを返します。

変更するノードのプロパティはhttp requestノードtemplateノードになります。

http requestノードのプロパティ

REST APIへのアクセスを行う機能を持ちます。プロパティ内のURL欄にREST APIへのアクセスを行うためのURLを入力します。

また、出力形式もJSONオブジェクトにすることも忘れずに行います。

f:id:ueponx:20210822193419p:plain

templateノードのプロパティ

先程のtemplateノードの内容を変更します。先程までhello worldとなっていた部分を以下の様に変更します。

f:id:ueponx:20210815235408p:plain

REST APIで表示しようとしているデータは{}で囲まれています。この記法のことをmustache記法と呼びます。mustacheという英単語は口ひげという意味になりますが、{}の括弧の形が口ひげの形に似ているということから、このようにいうようです。

このようにして、前段のノードから受信したデータをmustache記法で書いていくことで、HTMLへデータを動的に入れていくことができるようになります。

f:id:ueponx:20210822193527p:plain

このフローをデプロイして、ブラウザからアクセスを行うと以下の様に表示されます。デベロッパーツールでもREST APIの結果がHTML上に格納されているのがわかります。

フローのソースコード

[{"id":"1e264557b6c53ab9","type":"template","z":"57de3fc7e3549d4b","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<html>\n<head>\n<title>Sample Page</title>\n</head>\n<body>\n{{payload.stateName}}</br>\n{{payload.city}}</br>\n{{payload.street}}</br>\n</body>\n</html>","output":"str","x":220,"y":300,"wires":[["df967681dbf8cdc7"]]},{"id":"25b6a7546553433d","type":"http in","z":"57de3fc7e3549d4b","name":"","url":"/helloworld","method":"get","upload":false,"swaggerDoc":"","x":160,"y":180,"wires":[["0d0ef7452ddd9a69"]]},{"id":"df967681dbf8cdc7","type":"http response","z":"57de3fc7e3549d4b","name":"","statusCode":"","headers":{},"x":310,"y":360,"wires":[]},{"id":"0d0ef7452ddd9a69","type":"http request","z":"57de3fc7e3549d4b","name":"","method":"GET","ret":"obj","paytoqs":"ignore","url":"http://api.thni.net/jzip/X0401/JSON/460/8508.js","tls":"","persist":false,"proxy":"","authType":"","x":230,"y":240,"wires":[["1e264557b6c53ab9"]]}]

REST APIURIを変更することはできるのか

先程の例でHTMLをREST APIを使用することで動的に変えることができるようになりました。このままではREST APIのアクセス先を変更ができていません。そのため、次の例ではHTTPのリクエストによってREST APIでのアクセス先を変更してみようと思います。

今回使用しているREST APIでは郵便番号の前半3桁後半4桁の2つを使用していました。この2つを指定する方法を2つ出してみます。

パラメータで指定する場合

パラメータはURLの要素として出てきたものを取り出す方法になります。 方法としてはリクエスト時のURLの場所を指定して、それをパラメータと使用するというものです。やり方は簡単で、リクエスト時のURLの特定の箇所に:〇〇という形で埋め込むことで後続のフローで変数的に取得する事ができます。

先程のフローから赤い枠で指定した部分を変更していきます。

フロー f:id:ueponx:20210823001835p:plain

パラメータの設定は先程説明した通り、URLの特定の箇所に:〇〇(コロンを忘れずに)を入れるだけです。 今回は/api/郵便番号の前半3桁/郵便番号の後半4桁という形式でアクセスをしてもらおうと思います。http inノードのプロパティの設定はメソッドをGET、URLは/api/:query1/:query2としておきます。

http inノードのプロパティ f:id:ueponx:20210823002330p:plain

リクエストで受けた、郵便番号を取り出してhttp requestノードのURLに埋め込んで行きます。指定されたパラメータはmsg.req.paramsの中にパラメータ名で格納されているので、それをmustache記法で埋め込みます。

http requestノードのプロパティ

f:id:ueponx:20210823002333p:plain

メソッドもGETにしておくのを忘れずに。設定が完了したらデプロイをして、クライアントからブラウザでアクセスします。

(例)アクセスURL(サーバ部分は個々に違うので修正を行ってください)

f:id:ueponx:20210823005419p:plain

実行結果

f:id:ueponx:20210823005424p:plain

フローのソースコード

[{"id":"91514f2b09370b0e","type":"http in","z":"7caeabc7e2fd0b0d","name":"","url":"/api/:query1/:query2","method":"get","upload":false,"swaggerDoc":"","x":150,"y":240,"wires":[["6a741225e22b8501"]]},{"id":"67ae79a2ba4538aa","type":"template","z":"7caeabc7e2fd0b0d","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<html>\n<head>\n<title>Sample Page</title>\n</head>\n<body>\n{{payload.stateName}}</br>\n{{payload.city}}</br>\n{{payload.street}}</br>\n</body>\n</html>","output":"str","x":300,"y":300,"wires":[["f9680de638a24949"]]},{"id":"f9680de638a24949","type":"http response","z":"7caeabc7e2fd0b0d","name":"","statusCode":"","headers":{},"x":490,"y":300,"wires":[]},{"id":"6a741225e22b8501","type":"http request","z":"7caeabc7e2fd0b0d","name":"","method":"GET","ret":"obj","paytoqs":"ignore","url":"http://api.thni.net/jzip/X0401/JSON/{{{req.params.query1}}}/{{{req.params.query2}}}.js","tls":"","persist":false,"proxy":"","authType":"","credentials":{},"x":450,"y":240,"wires":[["67ae79a2ba4538aa"]]}]

クエリパラメータで指定する場合

もう一つの方法はクエリパラメータの形式で情報を引き渡します。こちらのほうが一般的かもしれません。ただ、Node-REDのフローを見ても、クエリパラメータの指定があるように見えないというのがちょっとだけわかりにくいかなと思います。

先程まで使用していたフローを以下の様に修正していきます。

f:id:ueponx:20210823004737p:plain

http inノードでクエリパラメータを受け取りますが、プロパティの設定は特に必要ありません。

http inノードのプロパティ

f:id:ueponx:20210823004740p:plain

処理を記述しませんが、アクセス時にクエリパラメータをつけておくことで自動的にノード間の通信を行うJSONに格納されます。パラメータはmsg.req.queryの中に格納されているので、それをmustache記法で埋め込みます。

http requestノードのプロパティ

f:id:ueponx:20210823004743p:plain

設定が完了したらデプロイをして、クライアントからブラウザでアクセスします。

(例)アクセスURL(サーバ部分は個々に違うので修正を行ってください)

f:id:ueponx:20210823004618p:plain

実行結果

f:id:ueponx:20210823004621p:plain

フローのソースコード

[{"id":"8467c21d2545a7c7","type":"template","z":"7caeabc7e2fd0b0d","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<html>\n<head>\n<title>Sample Page</title>\n</head>\n<body>\n{{payload.stateName}}</br>\n{{payload.city}}</br>\n{{payload.street}}</br>\n</body>\n</html>","output":"str","x":300,"y":120,"wires":[["ca35503b3c8beeb4"]]},{"id":"25b6a7546553433d","type":"http in","z":"7caeabc7e2fd0b0d","name":"","url":"/api","method":"get","upload":false,"swaggerDoc":"","x":100,"y":60,"wires":[["3f1735bf6de77b35"]]},{"id":"ca35503b3c8beeb4","type":"http response","z":"7caeabc7e2fd0b0d","name":"","statusCode":"","headers":{},"x":490,"y":120,"wires":[]},{"id":"3f1735bf6de77b35","type":"http request","z":"7caeabc7e2fd0b0d","name":"","method":"GET","ret":"obj","paytoqs":"ignore","url":"http://api.thni.net/jzip/X0401/JSON/{{{req.query.query1}}}/{{{req.query.query2}}}.js","tls":"","persist":false,"proxy":"","authType":"","credentials":{"user":"","password":""},"x":310,"y":60,"wires":[["8467c21d2545a7c7"]]}]

おわりに

質問が出たときにもう少しちゃんと答えられればと思ったのですが、自分でやってみると結構深い内容なのかもしれないと思いました。このぐらいの内容であれば1回位のハンズオンの内容になるのかもしれません。基本的な動きとはいえ、覚えておいて損のないないようだと思いました。