URL ルーティング
届くのは /users/42/edit という文字列 1 本だけ
ブラウザからサーバーに届くのは、/users/42/edit のような文字列です。これを見て、ユーザーの編集画面を組み立てる処理を呼ぶ。その対応を誰かが決めない限り、サーバーは何も呼べません。この振り分けをルーティングと呼びます。
MVC は、振り分け先の処理を Controller、データの読み書きを Model、画面の組み立てを View と呼び分けた整理です。ルーティングはその入口にあたる部分で、今回はここだけを扱います。
対応を 1 行ずつ手で書くこともできますが、それでは /users/1 /users/2 と ID の数だけ行が要ります。実際のルーターは URL を区切って、位置ごとに何が来ているかで行き先を決めます。
区切ると、先頭に空文字が生まれる
区切り文字はスラッシュです。ただし素直に区切ると、想定していないものが混ざります。
JavaScript
"/articles/7/edit".split("/")
// ["", "articles", "7", "edit"]URL はスラッシュで始まるので、その手前に「何もない」部分があると解釈され、空文字が 1 つ増えます。この状態で「1 個目がリソース名」と数えると、全部 1 つずつずれます。末尾にスラッシュが付いた /articles/ でも同じことが後ろで起きます。
空の要素を落としてしまえば、どちらの書かれ方でも同じ形になります。
JavaScript
"/articles/7/edit".split("/").filter(p => p.length > 0)
// ["articles", "7", "edit"]こうすると、要素が何個あるかと、何個目に何が入っているかだけで判断できます。今回の課題で返す文字列は次の対応です。
| URL | 返す文字列 |
|---|---|
/users | Users#index |
/users/new | Users#new |
/users/42 | Users#show |
/users/42/edit | Users#edit |
2 個に割れたときだけ、2 個目の中身を見ないと決まりません。new なら新規作成の画面、それ以外は ID とみなして詳細です。/users/new を先に詳細と判定してしまうのがいちばん多い間違いなので、new の確認を ID とみなす処理より前に置いてください。
リソース名を Controller の名前に直す
URL には articles と小文字で書かれていますが、返す文字列の側は Articles です。先頭の 1 文字だけを大文字にします。
Python
"member".capitalize() # 'Member'JavaScript
const s = "member";
s.charAt(0).toUpperCase() + s.slice(1); // 'Member'JavaScript には 1 つで済むメソッドがないので、先頭 1 文字と 2 文字目以降を分けてつなぎます。Python の capitalize は 2 文字目以降を小文字に直してしまうので、userProfile のような綴りを渡すと Userprofile になります。今回の入力は全部小文字なので、そのまま使えます。
やってみよう
route(url) を完成させてください。
- スラッシュで区切り、空の要素を捨てる
- 残った要素の個数で分ける。3 個のときは末尾が
editかどうかまで確かめる - リソース名の頭文字を大文字にして、
#の後ろに続く名前とつなぐ - 上の表のどれにも当てはまらなければ
404を返す
/users/42/delete のように、3 個に割れても末尾が edit ではない URL がありえます。個数だけで edit と決めつけないでください。
要件
- URL を
/で分割し、空要素を捨ててから判定する - リソース名の頭文字を大文字に変換する(
users→Users) - ルールにマッチしない URL は
'404'を返す
入出力例
route("/users") → "Users#index"
route("/users/42") → "Users#show"
route("/users/new") → "Users#new"
route("/users/42/edit") → "Users#edit"
route("/posts/9") → "Posts#show"
route("/posts/9/edit") → "Posts#edit"
route("/users/42/foo") → "404"
route("/") → "404"
route("/users/") → "Users#index"