Express Generator で作成されたファイルを触って Express を理解したい3：use,get,post,all,パスの表記

# 主旨

Express 4.x が対象です。`express app_name --view=pug` として生成されたファイルを見ながら、`express` の仕組みを理解していく。

- この時事は、[Express Generator で作成されたファイルを触って Express を理解したい2：router, pug](https://qiita.com/mml/items/3cc90479df033c0998e4) の続きです。前回の記事では、テンプレートエンジン (pug)、ルーティングについて記述しています。
- この記事では、`app`、`router` の all, get, all などのメソッドの違いと、パスの表記について記述しています。

公式ドキュメントの対応箇所は以下です：
- [Express アプリケーションで使用するミドルウェアの作成](https://expressjs.com/ja/guide/writing-middleware.html)

## 前提

以下の記事は、`express-generator` を使って下記のコマンドで `app.js` 以下のファイルを生成していると想定している。

```
$ express appname --view=pug
```

生成されたファイルは以下の通り。

```
project_dir
├── app.js          # アプリのメインファイル
├── bin
│   └── www            # yarn start 時に node bin/www として実行されるファイル
├── package.json       # ライブラリ等の依存関係やバージョン情報を格納したファイル
├── public          # static なファイルを置くフォルダ
│   ├── images         # http://localhost:8000/images 
│   ├── javascripts    # http://localhost:8000/javascripts 
│   └── stylesheets    # http://localhost:8000/stylesheets
│       └── style.css  # http://localhost:8000/stylesheets/style.css
├── routes          # router (ミドルウェア) 置き場
│   ├── index.js       # http://localhost:8000/ (トップページ)
│   └── users.js       # http://localhost:8000/users
└── views           # テンプレートファイル置き場
    ├── error.pug      # エラー時のテンプレート
    ├── index.pug      # index.js 用のテンプレート
    └── layout.pug     # index.pug や error.pug に読みこまれるテンプレート
```

実行時には 8000 番ポートを指定して使うものとする。

```
$ PORT=8000 yarn start
```

実行後、ブラウザで `http://localhost:8000` にアクセスしたときに、下記の表示になっているものとする。

<img src="https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F266247%2F8389c9ab-7a60-9055-def6-ccf760c1c19d.png?ixlib=rb-1.2.2&auto=format&gif-q=60&q=75&s=f25aa30c763746f5bfcaccc475857e38" width="60%"/>

# app / router の関数

よく使うのは以下の5つくらい。

- app.use
- app.get
- app.post
- app.route
- app.all

## get, post, all 関数

`app.get` と `app.post` は、それぞれ HTTP リクエストの "GET" と "POST" に対応している。`app.use` は GET も POST も拾うが、`app.get` の場合は GET リクエストだけを拾う。

```.js
app.get('/', function(req, res){
  res.send("GET");
});

app.use('/', function(req, res){
  res.send("USE");
});
```
たとえば、上記のコードの場合、curl でアクセスすると、下記のような結果になる。

```
$ curl -X GET https://localhost:8000
GET
$ curl -X POST https://localhost:8000
USE
```

use と get の行の順序を逆にすると、

```
$ curl -X GET https://localhost:8000
USE
$ curl -X POST https://localhost:8000
USE
```

こうなる。`use` は GET も POST も両方拾っており、`get` は GET だけを拾っている。

`all` はすべてのリクエストを拾う。`use` と似ているが、パスにマッチしたあとの挙動が少し異なる。

```.js
var router = express.Router();

router.use('/aaa', function(req, res) {
    res.send('aaa');
});

router.use('/all/bbb', function(req, res) {
    res.send('bbb');
});

app.use('/use', router );
app.all('/all', router );
```

## route 関数

`route` は少し特殊で、下記のような書き方ができる。

```.js
app.route('/')
  .post(function(req, res){
    res.send("POST");
  })
  .get(function(req, res){
    res.send("GET");
  })
  .all(function(req, res){
    res.send("ALL");
  });
```

ひとつのパスに対して、複数のリクエストを処理する関数を定義できる。上のコードの場合、下記の実行結果になる。

```
$ curl -X GET https://localhost:8000
GET
$ curl -X POST https://localhost:8000
POST
$ curl -X PUT https://localhost:8000
ALL
```

`.get`, `.all` などの関数の順序は、実行結果に影響する。先に書かれたリクエストの処理が優先される。下記のように記述すると、

```.js
app.route('/')
  .all(function(req, res){
    res.send("ALL");
  })
  .post(function(req, res){
    res.send("POST");
  })
  .get(function(req, res){
    res.send("GET");
  });
```

実行結果は下記のようになり、all で定義された関数が、他の関数より優先されて実行される。

```
$ curl -X GET https://localhost:8000
ALL
$ curl -X POST https://localhost:8000
ALL
$ curl -X PUT https://localhost:8000
ALL
```

## app と router の違い

`get`, `use` などの関数については、`app` (express) と `router` (experss.Router) に特に違いはない。`app` と `router` の大きな違いは、**`router` は `app` の関数の引数にできるが、その逆はできない**という点である。だから、app 内からミドルウェアを呼びだす時は `router` を使いましょう、ということらしい。

で、`app` はアプリケーションに一つしか存在できないからそうするものだと思ってたけど、下記のようなコードを書いたら問題なく動いてしまった。

```app.js
var app = express();
var app2 = express();
app2.use( '/bbb', function(req, res){ res.send('app2')});
app.use( '/aaa', app2 );
```

実行すると、下記のようになる。

```
$ curl -X GET http://localhost:8000/aaa
エラー
$ curl -X GET http://localhost:8000/aaa/bbb
app2
$ curl -X GET http://localhost:8000/bbb
エラー
```

`app2` を `router` に変更しても同じ挙動になる。あれ、じゃあ `router` 使う意味って何だろう。そもそも`app` を二つ作ったら、何か混乱が起こるかもしれないと思っていたけど、上の結果を見ると問題なさそう。先に作った `app` の `use` がちゃんとルートを認識していて、`app2` は `app` の子になっていて、問題なく動いている。(`app2` は exports されてないから当然といえば当然)

いろいろ調べてみた結果、明確な情報は見つけられなかった。おそらくだけど、`app` より `router` のほうがコンパクトなオブジェクトになっているので、`app` を使うより `router` を使うほうがリソースが節約できる、ということだと思う(完全に推測です)。

## 余談: app.use のパス表記

パス (ルートパス) とは、たとえば http://localhost:8000/xxxx/yyyy/zzz.html?aaa=bbb&ccc=ddd という URL でサーバにアクセスされたときに、 `/xxx/yyy/zzz.html` の部分を解析するために使われる文字列のことを言う。`app.use` や `app.get` などのルートハンドラーの第一引数として指定される。

たとえば、下記の `/`、`/users` はいずれも「ルートパス」にあたる。

```app.js
app.use('/', indexRouter);
app.use('/users', usersRouter);
```

この「(ルート)パス」の表記は、bash などで使える正規表現や、XPATH とも違うし、JSON のパス表記とも微妙に違うという、やや独特な表記法を使うようになっている(どうしてこうなったの?)。そのため、要点を押さえていないと、まったく想定していない URL にマッチしてしまったり、マッチさせたい URL にどうしてもマッチしてくれなかったりする。

ここでは、間違いやすいルートパスの表記についてのみ記載する。ルートパス表記に関する詳しいドキュメントは下記にある。

ルーティングに関する公式のドキュメント：
- https://expressjs.com/ja/guide/routing.html

ルートパスのマッチングの詳細:
- https://www.npmjs.com/package/path-to-regexp

なお、この記事や関連記事では、ルートパスのことを単に「パス」と書いている(混乱がない限り)。

## 余談: app.use のパスのマッチング

以下は、`PORT=8000 yarn stat` としてアプリを実行しているものとする。なお、以下は `app.use` を使用する場合であって、後述するように **`app.all` や `app.get` などは下記とは異なるマッチ**をする。これは結構な罠なので要注意!

- `app.use('/', ... )` はあらゆる URL にマッチする。
- `app.use( ... )` のようにパスを記述しない場合も、`app.use('/', ... )` と記述されたものとして、あらゆる URL にマッチする。
- `app.use('/aaa', ... ) ` は "/aaa" という URL か、"/aaa/" で始まる URL にマッチする。
 - http://localhost:8000/aaa
 - http://localhost:8000/aaa/bbb
 - http://localhost:8000/aaa/bbb/ccc.html
- 下記にはマッチしない。
 - http://localhost:8000/bbb/aaa
 - http://localhost:8000/aaaa
- `app.use('/aaa/', ... )` は `app.use('/aaa', ... ) ` と書くのと一緒。
- `app.use('/aaa*', ... )` は "/aaa" という文字列で始まるすべての URL にマッチする。"/aaa" の直後に別の文字列が続いてもいい。下記のいずれもマッチする。
 - http://localhost:8000/aaa
 - http://localhost:8000/aaabbb
 - http://localhost:8000/aaa.html
 - http://localhost:8000/aaaaa/bbb
 - http://localhost:8000/aaabb/ccc.html
- 下記にはマッチしない。
 - http://localhost:8000/xxxaaa
 - http://localhost:8000/aa
 - http://localhost:8000/bbb/aaa
- `app.use('/aaa*/bbb', ...)` は URL が "/aaa" で始まって、末尾が厳密に "/bbb" で終わる場合にのみマッチする。下記の URL にはマッチする。
 - http://localhost:8000/aaa/bbb
 - http://localhost:8000/aaaxxx/bbb
 - http://localhost:8000/aaaxxx/ccc/bbb
- 下記にはマッチしない。
 - http://localhost:8000/xxxaaa/bbb
 - http://localhost:8000/aaa/bbbccc
 - http://localhost:8000/aaa.html
- `app.use(/aaa/, ...)` は "aaa" を含むあらゆる URL にマッチする。下記のいずれもマッチする。
 - http://localhost:8000/aaa
 - http://localhost:8000/bbb/aaa
 - http://localhost:8000/ccc/aaa.html
 - http://localhost:8000/aaaaaaa
 - http://localhost:8000/aaabbb

最後の例のように、正規表現をパスとして使う時はシングルクオート `''` は書かない。一見して、正規表現を表わす `/` がパスの区切りの `/` と同じなので、つい `'/aaa/'` と書いてしまったりする。逆に  `'/aaa/'` とするべきところを `/aaa/` と書いてしまってハマる(ハマった)。

正規表現まわりについては、言語によって違う部分も多いので[公式のドキュメント](https://www.npmjs.com/package/path-to-regexp)をよく読もね(自戒)。

下記も参考までに；
- [基本的なルーティング](https://expressjs.com/ja/starter/basic-routing.html)

## 余談: Router を使う場合のパス表記

`app.use` と `router.use` を使って、二段階のパスのマッチをするように処理を書くと、`app.use` に直接 `function` を書いた場合とは異なる挙動をする。

以下、例示してみる、

```app.js
app.use('/aaa', function(req, res){
    res.send('超Lチカ');
});
```

`routes/app.js` に上記のような `/aaa` のパスに対する処理の記述がある場合、下記の URL にアクセスしたときのみ、ブラウザに "超Lチカ" と表示される。

 - http://localhost:8000/aaa

ここで、下記のように書きかえて実行してみる。

```app.js
var router = express.Router();

router.use(`/`, function(req, res){
    res.send('超Lチカ');
});

app.use('/aaa', router );
```

このようにしても、http://localhost:8000/aaa にアクセスしたときに  "超Lチカ" と表示される。
これは、以下のように処理がなされるためである。

- `app.use('/aaa', ...)` でまず `/aaa` にマッチする。
-  URL がそのまま `router` に送られる、
- `router` 側の `router.use('/', ... )` の行では、送られてきた URL から、送り元(親)の app でマッチした文字列("/aaa" の部分)を取り除いた上で、パスのマッチ処理をする。
- その結果 "/" にマッチする。(URL が空の場合は "/" と見なされる)
- `function` 内の `res.send` が実行される。

これをさらに、下記のように書きかえてみる。

```app.js
var router = express.Router();

router.use(`/bbb`, function(req, res){
    res.send('超Lチカ');
});

app.use('/aaa', router );
```

この場合、http://localhost:8000/aaa/bbb にアクセスしたときのみ "超Lチカ" と表示される。一方で http://localhost:8000/aaa はエラーが表示される。

`app.use('/aaa', ...)` で "/aaa" にマッチしているにも関わらずエラー表示が出てしまうのは、次のように処理がなされるからである。

- `app.use('/aaa', router );` で第一引数の '/aaa' が URL の "/aaa" の文字列にマッチして、`router` が呼びだされる。
-  URL がそのまま `router` に送られる、
- `router` 側の `router.use('/', ... )` の行では、送られてきた URL から、送り元(親)の app でマッチした文字列("/aaa" の部分)を取り除いた上で、パスのマッチ処理をする。
- `router` には `router.use('/', ... )` という記述がない。つまり、"/" というパスにマッチする処理がないため、何の処理も行なわれない。(空の URL は "/" と見なされる)
- この結果 `app.js` に処理が戻る。`app.use('/aaa', router );`の行の処理は、この時点で終わる。
- `app.js` の下記のコードで "/aaa" にマッチして (パスが指定されていないため、すべてのパスにマッチする)、404 エラーが生成される。

```.js
app.use(function(req, res, next) {
  next(createError(404));
});
```
- 最終的に、`res.render(`error`)` の行まで処理が進んで、エラー画面がブラウザに表示される。

`app.use` 側でパスにマッチしても、処理先の `router` 側に書かれているいずれのパスにもマッチしない場合は、あたかも `app.use` で何もマッチしなかったかのような挙動になる。

この例で、"/aaa" に対しても処理を行ないたい場合は、`router` 側に "/" のパスに対する処理を書くか、`app.js` 側にさらに "/aaa" に対する処理を書くかの、いずれかを行なう必要がある。

前者の場合は、下記のようにする。

```app.js
var router = express.Router();

router.use(`/bbb`, function(req, res){
    res.send('超Lチカ');
});

// 以下の行を追加
router.use(`/`, function(req, res){
    res.send('ただのLチカ');
});

app.use('/aaa', router );
```

後者の場合は、下記のようにする。

```app.js
var router = express.Router();

router.use(`/bbb`, function(req, res){
    res.send('超Lチカ');
});

app.use('/aaa', router );

// 以下の行を追加
app.use(`/aaa`, function(req, res){
    res.send('ただのLチカ');
});

```

いずれの場合でも、http://localhost:8000/aaa にアクセスしたときに「ただのLチカ」と表示されるようになる。同じ結果にはなるけど、パスの処理を階層化するという観点からは、前者のほうが良い気がする。

`app.use` の第二引数で `router` を指定した場合は、マッチの処理そのものが `router` 側に移譲される形になる。`router` 側でのマッチ処理の結果、いずれのパスにもマッチしないと、あたかも `app.use` 自体でマッチしなかったかのように、処理がスルーされるという結果になる。

このような、`app` と `router` のマッチの挙動を理解していないと、予想外のマッチが起こったり、どうしてもマッチしないというバグに繋りやすい。一方で、この仕組みを十分に理解していれば、`app.js` の記述を減らして、パスのマッチの記述を router 側のコードに効率的に分散することができる。

## 余談: app.get や app.all でのパスのマッチ

`app.get` や `app.all` は、`app.use` とマッチのパスの処理がかなり異なっている。

たとえば、下記のコードで、curl でいろいろリクエストを送ると…

```app.js
app.get('/aaa', function(req, res){
    res.send('getのLチカ');
});

app.use('/bbb', function(req, res){
    res.send('useのLチカ');
});
```

```
$ curl -X GET http://localhost:8000/aaa
getのLチカ
$ curl -X GET http://localhost:8000/bbb
useのLチカ
$ curl -X GET http://localhost:8000/aaa/ccc
-> エラー
$ curl -X GET http://localhost:8000/bbb/ccc
useのLチカ
```

このようになる。これは、下記の理由による。

- `app.use('/aaa', ...)` は "/aaa", "/aaa/bbb", "/aaa/ccc/bbb" のいずれにもマッチする。
- `app.get('/aaa', ...)` は **"/aaa" にしかマッチしない。**

次に、下記のようなコードを書いて curl で試してみる。

```.js
var router = express.Router();

router.get('/', function(req, res) {
  res.send('get:/');
});

router.use('/', function(req, res) {
res.send('use:/');
});

app.get('/get', router );
```

```
$ curl -X GET http://localhost:8000/get
use:/
```

`router.get('/'、 ...)` がスルーされて `use` でマッチしている。一方で、下記のようにすると `get:/get` が表示される。

```.js
var router = express.Router();

router.get('/', function(req, res) {
  res.send('get:/');
});

router.get('/get', function(req, res) {
  res.send('get:/get');
});

router.use('/', function(req, res) {
  res.send('use:/');
});

router.use('/get', function(req, res) {
  res.send('use:/get');
});

app.get('/get', router );
```

```
$ curl -X GET http://localhost:8000/get
get:/get
$ curl -X GET http://localhost:8000/get/get
エラー
```

さらに、下記のように `app.get` を `app.use` にすると、'get:/' が表示される。

```.js
var router = express.Router();

router.get('/', function(req, res) {
  res.send('get:/');
});

router.get('/get', function(req, res) {
  res.send('get:/get');
});

router.use('/', function(req, res) {
  res.send('use:/');
});

router.use('/get', function(req, res) {
  res.send('use:/get');
});

app.use('/use', router );
```

```
$ curl -X GET http://localhost:8000/use
get:/
$ curl -X GET http://localhost:8000/use/get
get:/get
```

これらのことから、`app.get` と `app.use` のパスのマッチの処理は、以下のように行なわれていていると推測できる。

- `app.use` は、'/use' のパス記述で "/use" にも "/use/get" にもマッチする。
- `app.get` は、'/get' のパス記述で "/get" にはマッチするが **"/get/get" にはマッチしない。**
- `app.use` の第二引数の `router` の中では、`app` 側でマッチした文字列 (が取り除かれたかのような状態で扱われる。(`router` 側では "/use/get" から先頭の "/use" が取り除かれて、"/get" であると見なされる)
- ただし `router.use` に限り、 `app.get( path, router )` とされていても、`app.use( path, router )` とされていても、`app` 側でマッチした文字列が URL から取り除かれているかのようにして、パスのマッチを行なう。
- `router.get` では、`app.get( path, router )` とされたか、`app.use( path, router )` とされたかでパスのマッチの挙動が変わる。

このように、複雑なことになっている。そもそも、get や all は、パスの処理を行なう router を呼びだすことは前提とされてない実装になってるんだと思う (ドキュメントの use 関数の記述にそんな雰囲気を感じる…むしろ明言してほしいところ)。

`app.get` の中で、パスの処理をするような `router` 呼び出すと、上記のようにパスの評価で混乱がおこるので、全くオススメできない。`app` 内で `get` を使う場合は「内部でパスの処理を行なわず、なおかつ next で処理を続けるようなミドルウェア(関数)」 (logger のような)の使用に限定しておいたほうが安全そうでだ。(下記参照)

- https://expressjs.com/en/4x/api.html#app.all

なお、`app.all` や `app.post` なども `app.get` と同じ挙動をする。

(Express 5.x になったらこのあたりは整理されるのだろうか…)

## 余談: URL 中のファイル名を変数に読みこむ

マッチしたパスの一部を、変数として取り出すこともできる。

```app.js
app.use('/:val', function(req, res){
  res.send(req.params.val);
});
```

`app.js` の `app.use('/', ... )` の記述を上記のように書きかえて、`PORT=8000 yarn stat` としてから、http://localhost:8000/hoge にブラウザでアクセスすると、下記のように表示される。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/b5fdb905-6b1a-1b76-f63d-c8089a6d9bb7.png" width=60%"/>

パスの中に `:val` と記述しておくと、その部分にマッチした文字列をプログラム中から `req.params.val` という変数で参照できるようになる。変数名は `val` でなくても良い。変数名には英数文字とアンダースコア `_` が使える。このような、URL の中から文字列を取りだせる仕組みのことは、**ルートパラメータ**と呼ばれる。

ルートパラメータは、パス表記の中に複数書くこともできる。たとえばこんな感じ。

```app.js
app.use('/aaa/:val/bbb/:file', function(req, res){
  res.send('val: ' + req.params.val + ', file: ' + req.params.file );
});
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/2208eef4-2b7a-7fa8-29b5-c436081e7547.png)

ルートパラメータを使うことで、URL で指定されたファイル名を読み出すようなこともできる。

詳しくは下記を参照のこと。
- https://expressjs.com/ja/guide/routing.html

## つづく?