Node-REDのHTTPノードでクライアントのIPアドレスを取得する

本来取得したかったのはNode-REDのdashboardに接続しているクライアントのIPアドレスだったのだが、HTTPノードを利用する方法に落ち着いた。

## 方法

http inノードでエンドポイントを作成し、functionノードで取り出す。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70022/b4831179-2231-aeed-ec9b-8003271e8c09.png)

```javascript:functionノードの中身
msg.payload = msg.req.ip;
return msg;
```

```json:フロー
[{"id":"9dad23b5.fd644","type":"http response","z":"4e3881be.18f2e","name":"","statusCode":"","headers":{},"x":550,"y":820,"wires":[]},{"id":"ea07abe6.815c98","type":"http in","z":"4e3881be.18f2e","name":"","url":"/clientip","method":"get","upload":false,"swaggerDoc":"","x":160,"y":820,"wires":[["a30561c.749e7a"]]},{"id":"a30561c.749e7a","type":"function","z":"4e3881be.18f2e","name":"set client ip to payload","func":"msg.payload = msg.req.ip;\nreturn msg;","outputs":1,"noerr":0,"x":360,"y":820,"wires":[["9dad23b5.fd644"]]}]
```

このフローをデプロイし、`http://[node-redのhost]/clientip` にアクセスすると、クライアントのIPアドレスが表示される。

## ポイント

http inノードから出てくるmsgをdebugノードで見てもmsg.res.ipという要素が見つからないのだが、Node-REDはHTTPにExpressを使っているのでExpressの要素が使えるらしい。

## 参考

* [Expressjsリファレンス req.ipの項](http://expressjs.com/en/api.html#req.ip)
* [Http in - remote address - Google グループ](https://groups.google.com/forum/#!topic/node-red/jc3WBHZMtSY)

## dashboardからこのAPIにアクセスする

さあ、HTTPのAPIができたのでdashboardでアクセスするか、と思い、下記のフローを作成。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70022/7f9614ca-6314-497e-e355-87219655d21e.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70022/f7350a89-13cf-8dac-e7e2-82930bfc0de2.png)

しかし、返ってきたクライアントのIPアドレスは `127.0.0.1`。Node-REDのホストから自分のAPIにHTTPリクエストしているので当然ですね…

仕方なくdashboardのtemplateノードを使って、ちゃんとクライアント側からHTTPリクエストが飛ぶようにした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70022/2b904c15-a530-3472-3895-02000ecb51e7.png)

```html:templateノードの中身
<script>
this.scope.action = function() {
    let xhr = new XMLHttpRequest();
    xhr.open("GET", "../clientip", false);
    try {
        xhr.send();
        if (xhr.status != 200) {
            return `Error ${xhr.status}: ${xhr.statusText}`;
        } else {
            return xhr.response;
        }
    } catch(err) {
        return "Request failed";
    }
}
</script>
<md-button ng-click="send({payload:action()})">
    Get IP Address
</md-button>
```

```json:フロー
[{"id":"5eb60b39.a89a64","type":"ui_template","z":"4e3881be.18f2e","group":"4e09a2b6.c91f8c","name":"[button]Get IP Address","order":2,"width":0,"height":0,"format":"<script>\nthis.scope.action = function() {\n    let xhr = new XMLHttpRequest();\n    xhr.open(\"GET\", \"../clientip\", false);\n    try {\n        xhr.send();\n        if (xhr.status != 200) {\n            return `Error ${xhr.status}: ${xhr.statusText}`;\n        } else {\n            return xhr.response;\n        }\n    } catch(err) {\n        return \"Request failed\";\n    }\n}\n</script>\n<md-button ng-click=\"send({payload:action()})\">\n    Get IP Address\n</md-button>","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":300,"y":1060,"wires":[["2f5c3897.ce4548"]]},{"id":"2f5c3897.ce4548","type":"debug","z":"4e3881be.18f2e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":510,"y":1060,"wires":[]},{"id":"4e09a2b6.c91f8c","type":"ui_group","z":"","name":"デフォルト","tab":"531fac22.ff06a4","disp":true,"width":"6","collapse":false},{"id":"531fac22.ff06a4","type":"ui_tab","z":"","name":"ホーム","icon":"dashboard","disabled":false,"hidden":false}]
```

これでちゃんとdashboardからクライアントのIPアドレスが取得できた。