Express web framework (Node.js/JavaScript)の勉強 part2
Creating route handlersを読みすすめる
前回、特定のパスへのリクエストを受け取り、対応するコールバック関数を実行する方法を学んだ。 今回は express.Router についてを学ぶ
Router
express.Router
を使うことで、特定のパスへのリクエストをまとめて扱うことが可能になる。
ここの例では http://localhost:3000/wiki/
以下へのリクエストを解釈するrouterインスタンスを作成している。
具体例として以下に wiki.js
というファイルを示す
wiki.js
// wiki.js - Wiki route module const express = require('express'); const router = express.Router(); // Home page route router.get('/', function(req, res) { res.send('Wiki home page'); }); // About page route router.get('/about', function(req, res) { res.send('About this wiki'); }); module.exports = router;
4行目 const router = express.Router()
express.Router を使うことでモジュール化されたのマウント可能なルートハンドラを作成できる。
別ファイルで特定のパス(今回は /wiki
プレフィックスとする)を指定することで、そのパスへ以下へのリクエストをルーティングするとが可能になる。
7〜9行目 router.get('/', function(req, res){ ...以下略
特定のパス以下の /
へのリクエストを第二引数の関数へルーティングする
今回の例では/wiki
へのリクエストをルーティングする
12〜14行目 router.get('/about', function(req, res){ ...以下略
特定のパス以下の /about
へのリクエストを第二引数の関数へルーティングする
今回の例では/wiki/about
へのリクエストをルーティングする
16行目 module.exports = router;
ここまでで作成したrouterオブジェクトを別ファイルで使用できるようにエクスポートする
app.js
const express = require('express'); const app = express(); const port = 3000; const wiki = require('./wiki.js'); app.get('/', (req, res) => { res.send('Hello World!') }); app.listen(port, () => { console.log(`Example app listening on port ${port}!`) }); app.use('/wiki', wiki);
5行目 const wiki = rquire('./wiki.js');
nodejsはCommonJSのモジュールシステムを採用している。
* 参考:What is require? | Node.js
CommonJSではrequireを使うことでモジュールを取り込む。
requireはモジュールの名前もしくはパスを引数にとる。
15行目 app.use('/wiki', wiki);
app
は関数以外にも、特定のミドルウェアの機能を特定のパスに関連付けることができる。
今回の例ではパス/wiki
とオブジェクトwiki
が、それぞれapp.use
の第一、第二引数に使われている。
/wiki/about
への要求はwiki
オブジェクトへ渡り、/wiki/about
からプレフィックス/wiki
を除いたパス/about
を使いwiki
オブジェクト内でマッチングが行われる。
結果wiki.js
の12行目で定義されたパスにマッチし、対応する関数が実行される。
まとめ
- routerは別ファイルにまとめることで、特定のパス以下の処理を分割することができる
Express web framework (Node.js/JavaScript)の勉強 part1
webアプリの知識がないことに対しての危機感と興味からExpress web framework (Node.js/JavaScript) - Learn web development | MDN を読んでみることにした。
個人的に気になったところや手順を残す。
私の開発環境は以下の通り
$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 20.04.1 LTS Release: 20.04 Codename: focal
Nodeのインストール
はじめに node をインストールする
私の場合は nodenv を使ってインストールした
Hello World
まずは導入のための章であるHelloworld Express の内容を理解する
const express = require('express'); const app = express(); const port = 3000; app.get('/', (req, res) => { res.send('Hello World!') }); app.listen(port, () => { console.log(`Example app listening on port ${port}!`) });
1行目 const express = require('express');
一行目の const express = require('express');
はモジュールをインポートするためのものである。
自作のファイルや、 node_modules
ディレクトリ以下に存在するモジュールをインポートできる。
自作ファイルをインポートする場合には、require(./path/to/myLocalModule);
のように、現在のファイルが存在するディレクトリからの相対パスで表現する。
この表記方法はUnixだけでなくWindowsでも可能と記載されている(手元にWindows環境がないため未確認)
参考
2行目 const app = express();
Expressアプリケーションを作成する。
関数express()
はモジュールexpress
が提供するトップレベルの関数とのことである。
app
オブジェクトは下記の目的のためのメソッドを持つ
この章では"httpリクエストのルーティング"機能を使用する
ちなみに変数名がappという名前は慣習らしい
参考
5〜7行目 app.get('/', (req, res) => { ...以下略
特定のパスへのHTTPのGETリクエストを、特定のコールバック関数へと誘導する。
今回の例ではhttp://localhost:3000/
に対してGETメソッドが呼ばれた場合に、無名関数(req, res) => { res.send('Hello World!')}
が呼ばれる
req
はHTTP Requestを、res
はHTTP Responseを表す。
res.send([body])
によってResponseのボディ部を作成(内容を登録)するようだ
参考
9〜11行目 app.listen(port, () => { ...以下略
3行目で指定されたport=3000を使ってbindし外部からの接続を待つ
第二引数はおそらくサーバが構築したばあいに実行されるコンソールへの出力と予想する
参考
補足:文字列中に変数の値を埋め込む方法
バッククォートをつかうとできるようだ
bashなどになれているため最初はダブルクォートで囲んでしまい、意図通り動かなくて悩んだ
参考
まとめ
- expressではappというオブジェクト作成し、そこに対して例えばgetメソッドを使うことで、特定のメソッドへのルーティングを実現する
device treeにおけるアンパサンド(&)の意味
はじめに
device treeを触っているときの疑問を解消する内容が下記リンクにかかれていた。
linux kernel - Meaning of an ampersand prefix in a device tree - Unix & Linux Stack Exchange
本記事は上記ページを備忘録を兼ねて翻訳しようと思う。
※英語が得意なわけではないので意訳を含みます。誤りがあれば指摘ください。
翻訳
質問
異なるノードを指定しようとするDTSファイルを眺めていたときに、興味深い、いくつかのノードの命名規則を見つけた。
/ { model = "TI AM335x BeagleBone Black"; compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; }; &ldo3_reg { regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-always-on; }; &mmc1 { vmmc-supply = <&vmmcsd_fixed>; }; &mmc2 { vmmc-supply = <&vmmcsd_fixed>; pinctrl-names = "default"; pinctrl-0 = <&emmc_pins>; bus-width = <8>; status = "okay"; }; / { hdmi { compatible = "ti,tilcdc,slave"; i2c = <&i2c0>; pinctrl-names = "default", "off"; pinctrl-0 = <&nxp_hdmi_bonelt_pins>; pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>; status = "okay"; }; };
ノードの先頭に&
がついている場合どのような影響があるのでしょうか?
- 上の例における
&ldo3_reg
、&mmc1
、&mmc2
のケースです
ノードはルートノードの中に存在できる一方で、ルートノードから分離されている必要性は何でしょうか?
- device treeでは
/
の中がルートノードとして表現されます - 上の例ではルートノードの中に、ノード
hdmi
が存在している一方で、&
から始まるノードは/{ };
の外側に記述されていることを述べていると予想します
興味深いことに、上の例では2つのルートノードが存在します。これは問題ないのでしょうか?
- 先頭と末尾の方に存在する2つの
/
を示していると予想します
回答
https://developer.toradex.com/device-tree-customization より
ノードはampersand(&)とラベルを用いて参照可能です
プロパティの上書き
プロパティを上書きする場合、ノードはampersandとラベルを使って参照される必要があります
後に記述されたdevice treeのエントリーは、それより以前に記述されたエントリーを上書きします(エントリの順番は重要であり、したがってインクルードの順番も重要です)
一般的に高いレイヤ(キャリアが作成するボードのためのdevice tree)は、低いレイヤ(例えばSoCのdevice tree)を上書きします。
- 高いレイヤ = dtsファイル、 低いレイヤ = dtsiファイル と考えれば良いかもしれません。
これを実現するために高いレイヤ(のdevice tree)は低いレイヤ(のdevice tree)を最初に取り込みます(includeします)
例えば、デバイスもしくはホストになれる(デュアルロールな)USBコントローラは、dr_modeをプロパティを使い明示的にデフォルトモードを上書きします
&usbdev0 { dr_mode = "host"; };
まとめ
プロパティの上書きのためには&label
の形式が必要ということを知った
ただ、上記がdevice treeの公式のページか不明なので、公式なページで明言されているならば知りたい