Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

More than 3 years have passed since last update.

新3-5.5 リソース指向ルーティング

Last updated at Posted at 2018-12-06

はじめに

今回は、ルーティングについて詳しく解説します。

いまは、ルーティングはURLとアクションを結びつけること、というざっくりした理解だと思います。
実際ほとんどそのとおりで、決して難しいものではありません。

しかし、これからアプリケーションを開発するにあたっては、ルーティングには気を遣わなければなりません。

もし好き勝手なURLを一見無関係なアクションに結びつけていたらどうなるでしょう?
https://geektwitter.com/happyというURLへのアクセスが、welcome#goldgymアクションを実行するとしたら?

法則のないルーティングをすると、ユーザーはもちろん、開発者自身が一番困ることになります。
そこにアクセスすると何が起こるか、予測がつかないのです。

https://geektwitter.com/happyは、世界平和をスローガンに掲げるGeekTwitter社の新サービスである「見るだけで脳に作用し幸せになるページ」を表示するかもしれません。

どのURLとアクションを結びつけたか全部覚えているからいいよ、と思う人もいるでしょう。
驚異の記憶力をもつあなたならそれでも問題ないでしょう。
しかしそのコードを他の人に見せるとき、はたしてすんなり理解してもらえるでしょうか?

ちなみに本家ツイッターでは、https://twitter.com/happy が有効なURLです。これはIDがhappyのユーザーのページを表示するアクションに対応しています。このユーザーは、執筆時点で凍結されていました。

幸い、ルーティングにはこうするとわかりやすく書けるという作法があります。
さらに、Railsはわかりやすい書き方が簡単にできるようサポートしてくれます。

これからその考え方を学びましょう。

概要

まず、Railsにおけるルーティングについて概観します。
次にリソース指向という概念について解説しながら、それをRailsで実現するしくみについて解説します。

ルーティング - config/routes.rb

URLとアクションの対応づけをルーティングといいました。
Railsでは、ルーティングは以下のようにconfig/routes.rbというファイルに記述して設定します。

config/routes.rb
Rails.application.routes.draw do
  get    'tweets'          => 'tweets#index'
  post   'tweets'          => 'tweets#create'
  get    'tweets/new'      => 'tweets#new'
  get    'tweets/:id/edit' => 'tweets#edit'
  get    'tweets/:id'      => 'tweets#show'
  patch  'tweets/:id'      => 'tweets#update'
  put    'tweets/:id'      => 'tweets#update'
  delete 'tweets/:id'      => 'tweets#destroy'
end

8つのURLがそれぞれのアクションに結びついているのがなんとなくわかるでしょう。
あとで解説しますが、これらはアプリケーションによく使われるルーティングです。

これを見るとふたつ気になる点があります。

まず、ルーティングはURLとアクションを結びつけるものだと言ったのに、URLがなんだか短いということです。
もう一つは、getとかpostとか、よくわからないのが左にくっついていることです。

順番に解説します。

短いURL - パス

これはURLではなくパスと呼ばれるものです。

ここでTwitterのURLをいくつか見てみます。

https://twitter.com/
https://twitter.com/i/notifications
https://twitter.com/i/moments
https://twitter.com/Twitter

全てにおいて https://twitter.com/ という部分が共通していますね。これはアプリケーション固有のものなので、同一のアプリケーション内で変わることはありません。

この共通部分は、開発時の環境とリリース後の本番環境で変わります(環境とは、アプリケーションを動かす場所のことです)。前者は自分のパソコンの中であり、後者はもっとたくさんのアクセスに耐えられる別のコンピュータです。

もしURLをすべて書いていたら、別の環境に移行するときにファイルを書き換えなければならないので、config/route.rbにはパスだけを記述しています。

全てのアクションに共通するパスをルートパスといい、/と表現されます。ルートとは英語でroot、つまり根であり、全てのパスの根本にあるという意味を持ちます(注: まぎらわしいのですが、ルーティングは英語でrouting、すなわち道をあらわすrouteであり、rootとは関係ありません)。

あらゆるパスは、ルートパスの下に連なるので、/tweetsのように書けます。

というわけで、config/routes.rbは、URLではなく、パスとアクションを結びつけています。

config/routes.rb
Rails.application.routes.draw do
  get '/tweets' => 'tweets#index'
  # 省略
end

getやpostなど - HTTPメソッド

HTTPメソッドと呼ばれるものです。
HTTPメソッドは好き勝手に設定できるものではありませんが、数種類しかないので覚えるのは難しくないでしょう。

実はアクションへのルーティングは、URLだけではなく、URLとHTTPメソッドの組み合わせによって決定されます。上の例でも、同じパスに対して2つのルーティングが設定されていますが、互いにHTTPメソッドが異なるために、別のアクションに結びつけることができます。

HTTPメソッドは、GETやPOSTのように大文字で表記されます。
config/routes.rbでは小文字で書かなければなりません。

リソース指向

このように、ユーザーのやりたいことを主語(リソース)と述語(HTTPメソッド)の組み合わせで表現しようという考え方を、リソース指向といいます。

**リソースとは、ユーザーが操作する対象(情報)すべてです。**猫も杓子もリソースです。

操作する対象は、データベースのテーブルであることもありますし、ある特定のレコードである場合もあります。データベースではなく、表示するページそのものを指すこともあります。
とにかく操作可能な情報すべてをリソースと言うのです。

以下にはじめに紹介したルーティングを表にまとめました。
これはリソース指向なルーティングになっています。

なお、リソースはパスで表現します。リソースは情報そのものであり、パスはリソースを指し示す名前というイメージです。

HTTPメソッド パス(リソース) アクション やりたいこと
GET /tweets tweets#index ツイート一覧のページを表示
POST /tweets tweets#create ツイートを投稿
GET /tweets/new tweets#new ツイートを新規作成するページを表示
GET /tweets/:id/edit tweets#edit ある一つのツイートの編集ページを表示
GET /tweets/:id tweets#show ある一つのツイートのページを表示
PATCH /tweets/:id tweets#update ある一つのツイートを更新
PUT /tweets/:id tweets#update ある一つのツイートを置換
DELETE /tweets/:id tweets#destroy ある一つのツイートを削除

tweets#updateアクションを実行するルーティングが2つ設定されています。これは見ての通りPATCHメソッドとPUTメソッドの違いによるのですが、ここでは投稿済みのツイートを更新するものということでほとんど同じと考えて結構です。

「ツイート一覧のページを表示」は、「/tweetsが表すリソースをGETすること」
「ツイートの投稿」は、「/tweetsが表すリソースにPOSTすること」
「ツイートの削除」は、「/tweets/:idが表すリソースをDELETEすること」

この考え方を身に着けていきましょう。

リソース指向なルーティングの実践

リソース指向は、単なる思想であり、作法です。

作法には、必ずこのように書かなければ動かないといった強制力はありません。しかし、多くのRailsエンジニアがこの作法を守って名前を決定しています。
誰もがリソース指向に基づいて名前をつけるなら、誰が書いたコードでもパスとHTTPメソッドを見るだけで、なんとなく何をするアクションに結びついているのかがわかるのです。

以下に、Railsでリソース指向なルーティングを実装するにあたってのルールやテクニックをまとめました。

  1. データベース上のリソースは/(テーブル名)/(+α)というパスで表現する
  2. ページを表示するアクションはすべてGETメソッドで呼び出す
  3. CRUD操作はresourcesを用いて書く
  4. アクション名はresourcesに従う
  5. resourcesで定義するルーティングを限定する

順番に見ていきましょう。

① データベース上のリソースは/(テーブル名)/(+α)というパスで表現する

繰り返しになりますが、リソースはユーザーが操作する対象(情報)すべてです。

例えば、/tweetsというリソースはtweetsテーブルを表し、tweets/:idtweetsテーブルの特定のレコードを意味します。

特定のツイートを指すための/tweets/:idというパスは一体何者でしょうか。

ツイートは、tweetsテーブル内のidカラムの値で識別されます。idが1のツイートは1つしかありません。
これを利用して、あるひとつのツイートを指すパスは、/tweets/1のようにidを埋め込むことで書けそうです。

idの数字が入っている部分は、当然idによって変わってしまいます。tweetsテーブルのレコードの数はどんどん変化するので、get '/tweet/1' => 'tweet#show'などとひとつひとつルーティングを設定するわけにもいきません。

そこで、get '/tweets/:id' => 'tweets#show'のようにルーティングを指定します。
これにより、/tweets/○○というパスで、○○の中に何が入っていようとそれをidとして認識するようになります。

パスに書かれたidの値は、対応するアクションの中でparamsという仕組みを使って受け取ることができます。

具体的な実装は、投稿機能の教材で解説します。

② ページを表示するアクションはすべてGETメソッドで呼び出す - リダイレクト

さきほどの表で赤色で示した部分を見てください。
GETメソッドに対応付けられたアクションの役割は、すべて「ページの表示」でした。

一方、GET以外のHTTPメソッドの名前には、それぞれのアクションの内容が表現されています。POSTやDELETEなどです。
これらのメソッドが結び付けられたアクションは、リソースの操作にのみ責任を負っています。

例えば、/tweetsというリソースにPOSTメソッドでアクセスした場合を考えましょう。これはツイートを新しく投稿することを意味し、先の表ではtweets#createアクションに結び付けられています。

tweets#createアクションは、文字通りツイートを投稿することだけを担当します。「リソースを投稿すること」というのがユーザーのやりたいことだからです。

とはいえアクションを実行したらその結果のページを表示しなければなりません。
ツイートが投稿された(あるいは投稿できなかった)ことをユーザーに知らせるためです。

そこで、GET以外のメソッドに対応付けられたアクションは、他のアクションにリダイレクトするという方法でページを表示します。

リダイレクトは、現在のアクションに続けて別のアクションを実行するための仕組みです。

下図の赤矢印で、tweet#createアクションは自分の役目を終え、ページの表示をtweet#indexアクションに委ねています。

リダイレクト.png

tweets#createアクションは**tweetsテーブルにレコードを追加するまでの責任を負い、
tweets#indexアクションは
tweetsテーブルから全てのレコードを取得して一覧ページを表示するまで**の責任を負います。

「新しいツイートを投稿する」というユーザーのやりたいことは、この2つのアクションが続けて実行されることで実現されていたのです。

ツイートの一覧画面は、例えばツイートを削除した後にも表示したいでしょう。その場合にも、tweets#destroyアクションからtweets#indexアクションへリダイレクトします。

リダイレクトを利用することで、リソースに対する操作だけを明確に切り分けて書くことができます。
また、「tweetsテーブルからツイートのレコードを取り出して表示する」という一連の処理を何度も書かずに済みます。必要なタイミングで、tweets#indexアクションへリダイレクトするということを書くだけです。

これも具体的には、投稿機能の教材で解説します。

③ CRUD操作はresourcesを用いて書く

ここまで読んで、なんだか複雑な感じがしたかもしれません。

細かいことは抜きにして、簡単に作法に従った書き方をする方法はないものでしょうか?あります。

実は、最初に8行も書いたルーティングは、以下の1行に置き換えられます。

config/routes.rb
Rails.application.routes.draw do
  resources :tweets
end

resources、リソースの複数形です。

よくよく考えてみれば、ひとつのテーブルに対する操作は、そんなに種類が無いような気がします。レコードの作成、読み込み、更新、削除などです。
そう、先に挙げた8つのルーティングは、テーブルを操作するアクションのほとんどを網羅しているのです!

resourcesのあとに:tweetsのようにコントローラー名を書くことによって、そのテーブルやレコードをリソースとする主要な8つのルーティングを定義することができます。

このリソースに対する基本的な操作を、作成(Create)、読み込み(Read)、更新(Update)、削除(Delete)の頭文字を取ってCRUDと言います。

アプリケーションはデータベースが主人公と言っても過言ではありませんし、そのテーブルに対するCRUDアクションのルーティングが一気に設定できるとあれば、これを使わない手はありません。
さらに、resourcesを用いることでエンジニアによるブレの生じない命名になり、良いことづくめです。

一方resourcesに含まれず、個別にルーティングを設定する必要があるアクションとしては、例えば検索機能のアクションが挙げられます。tweets#searchアクションなどを定義する必要があります。

他にも、https://geektwitter.com/happyのようなコマーシャルページや、ログインしていないユーザー向けのトップページなどの、データベースの操作を伴わない静的なページを表示するだけのアクションも、独自にルーティングを設定する必要があります。

④ アクション名はresourcesに従う

リソース指向とは直接関係のない話ですが、アクション名についても説明しておきます。

上に見たように、resourcesはテーブルやレコードをリソースとする主要な8つのルーティングを追加してくれます。
ルーティングと同時に、アクション名も決まっています。tweet#indextweet#createなどです。
resourcesを使ってルーティングを行えばこれらのアクション名に従わざるを得ません。

しかしよく理解しないうちは、ツイートの一覧ページを表示するアクションをtweets#topアクションに書いてしまう人も見られます。
そのため、自分でget '/tweets' => 'tweets#top'のようなルーティングを設定しなければなりません。

あるテーブルのレコードの一覧表示はindexアクションで行うのが通例であり、topのような別の名前で定義すると、他の人が見たときに余計な混乱を招きます。

resourcesで定義できるアクションを独自の名前で定義しないように注意しましょう。

resourcesで定義するルーティングを限定する

resourcesは便利なのですが、8つもルーティングを実装したくない場合もあります。
例えば、ツイートの編集機能は実装したくない!という場合、tweets#editアクションもtweets#updateアクションも必要ありません。

resourcesは、only`オプションを使うことで、定義するルーティングを限定できます。
以下は、編集機能を実装しない場合の例です。

config/routes.rb
Rails.application.routes.draw do
  resources :tweets, only: [:index, :create, :new, :show, :destroy]
end

#問題!〜ルーティングエラーを直そう〜
resourcesを設定すると今までのパスが変わってしまう部分が出てきてこのままではエラーが起こってしまうので適切なページを直してみましょう!

#ヒント
RoutingErrorが出た際にコマンドプロンプトまたはターミナルで$ rails routesと打ちましょう!
new_tweet GET /tweets/new(.:format) tweets#new
ここの部分のパスを見直してみよう!

#答え

ビュー - index.html.erb

app/views/tweets/index.html.erb
<h1>GeekTwitter</h1>
<h3>Tweet一覧</h3>
<%= link_to "新規投稿へ", new_tweet_path %>
<div class="twes-container">
  <% @burgers.each do |t| %>
    <div class="tweet">
      <%= t.body %>
      <%= t.created_at %>
    </div>
  <% end %>
</div>

<%= link_to "新規投稿へ", tweets_new_path %>
<%= link_to "新規投稿へ", new_tweet_path %>に直す!!
意外と簡単にエラーを解決できましたね!RoutingErrorが出たら$ rails routesを使ってパスを見直してみましょう!

おわりに

以上、ルーティングについての解説でした。

細かい話も多かったので、いっぺんに身につけるのは難しいかもしれません。必要に応じて読み返すようにしてください。

あなたがどんなパスをどんなアクションに結びつけようと、そのルーティングを妨げる人もプログラムもありません。

しかし、適当な命名をしていては誰も触れたくないわかりにくいアプリケーションができていくでしょう。

だからこそ今回解説した作法をよく学び、良いルーティングをすることを心がけなければなりません。

0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?