QA@IT

URL設計で悩んでいます。

3752 PV

現在、Railsで開発しているのですが、 URL設計で困った点があり質問させていただきます。

例を挙げて説明させていただきます。
Blogというリソースがあったとして、これにファイルからデータをインポートする機能を実装したいとします。
そのためには、アップするファイルを入力させるための画面の URL と、それをアップする際に指定する URLがいると思います。
その時の URL設計に悩んでいます。

パッと浮かんだのは
/blogs/import/
ですが、これは URLに動詞が入っているので正直いけてない気がします。

次に
/blogs/file/new#ファイル選択画面
/blogs/file(post) #アップ時のURL
を思いついたのですが、これだとまるで fileというリソースがDB上にある様に見えます。

この場合の正しい URL設計は何でしょうか?

回答

今回の場合は、htmlのフォームが吐き出す形以外の形でリソースをcreateするということなので

POST /blogs.csv # 拡張子はお好きに。

こんな形で、拡張子を活用してみるのはいかがでしょうか。
アクションが膨らむのは好きではないので、formatがcsvのときは別actionにするとかやると良い感じです。

newに相当する画面のURLには悩むところですが、

GET /blogs/new_csv

僕だったらこんな感じにします。

scaffoldが吐き出すnew/createアクションの場合
「new画面のフォームをもとにBlogというリソースを作成する」
ということをするので、"Blogというリソースを作成する"という部分が変わらないので、そこまで新しいURLを作成したくないなーというの本音です。

この場合の正しい URL設計は何でしょうか?

好みの問題もあると思うので唯一解とかはないと思います。
自分や自チームのメンバーが一番しっくり来るのがきっと正解。

編集 履歴 (0)

URLはGETがいちばん重要です。

Blogというリソースがあったとして、これにファイルからデータをインポートする機能を実装したいとします。

このファイルを指すURLが必要ですか?たとえば画像をアップロードしたあと、その画像自体を指すURLが必要な場合などです。
その場合は、a_matsudaさんの回答のattachmentsのような名前を採用すると、
GET /attachments/:idでファイルだけを取り出すことができます。(PUT, DELETEも可能になりますね)

それとも、ファイルはデータをインポートするのに使うだけで、そのデータはBlogに含まれてしまうのでファイル自体のURLは必要ないですか?
その場合は、moroさんの回答のように動作を指すimportのようなリソースを使うことで、

GET /imports/:id で進捗状況や実行結果を表示

ということができます。

この2つはリソースの性質が異なるので、用途によって使い分けるといいですね。

編集 履歴 (0)

まず、

これだとまるで fileというリソースがDB上にある様に見えます。

に関してですが、これはまったく気にしなくて良いと思います。
DBにinsertするかどうかはあくまでも内部の実装の話で、リソースの設計はシステム全体として何をインプットして何をアウトプットするか、という話なので、完全に対応している必要はありません。

Blogは例であるとのことなので、リソース名に食いつきすぎると脇道に逸れちゃうかもしれませんが、importはリソースの名前ではなくて動作なので、これは明らかに名前が悪いですね。
fileだとちょっと一般的すぎてなんだったかわからなくなりそうです。

自分だったら
resouces :attachments, only: [:new, :create]
にするかなぁ。次点でassetsぐらいですが、Rails本体のやつと混同しそう。

blogsにネストさせるかどうかは使い方次第なのでなんとも言えないですね。古い例ですが、Mephistoの実装ではassetsは独立したリソースになってました。 https://github.com/technoweenie/mephisto/blob/master/app/controllers/admin/assets_controller.rb

編集 履歴 (0)

/blogs/import

でいいとおもいますよ。

たかだか10種類ぐらいしかないHTTP methodsだけで、2万語以上存在する英語の動詞に存在する豊穣さを全て表現できると考えるほうが無理があると思います。

RESTful routingは「ボイラープレート的なCRUDを表現するときの意思決定を自動化してくれるconvention」程度にとらえて、そこからはみでたものは、躊躇せずに動詞が含まれるURLを定義していけばよいと思います。

個人的には、createをPOSTにマップしたりupdateをPUTにマップしたりするのも「名前重要」の文化圏の住人として、違和感を感じています。これはREST的な定義がどうこうとかいう以前に、純粋に英語の感覚として対応がストレートでない。rake routesやリファレンスなしにこのマッピングがなかなか頭に入ってこないのは、まさにこれが原因で、おおむね全体的に素晴らしいRailsのなかでルーティングは画竜点睛を欠いてると思っています。

とはいえ、ボイラープレート的なCRUDを扱う90%ぐらいのケースでは必要十分なので、使える場面では積極的に使い、使えない場面では無理しない、というのがよいのではないかと思っています。

編集 履歴 (0)
  • そうですね。自分の現在の状況としてはfileのimportにそこまで多数のロジックを実装しようとは考えてないので、シンプルに/importにしようかと思います。みなさん回答ありがとうございました。 -

やり方はいろいろありますが、データインポートなど複数のリソースに影響を及ぼす、バッチ的な動きをさせたい場合には「トランザクションリソースを作る」という考え方でリソース設計するようにしたところ、いろいろ捗りました。

たとえば今回の例でも「インポート処理: import_job」を作るという風に考えると、ルーティングは

resouces :import_jobs

としておき、

  • GET /import_jobs/new (ImportJobsController#new) でインポート用のフォームなどを表示し、
  • POST /import_jobs (ImportJobsController#create) で実際のインポート処理をはじめるなど、いろいろやりようがあります。

私が実アプリで取り組んだときは、インフラの都合上非同期にインポートジョブを実行させたかったので、createアクションの中でいったん必要な情報をDB(import_jobsテーブル)に保存しました。その後でワーカーに流し、

  • GET /import_jobs/:id で進捗状況や実行結果を表示

という作りにしました。ポイントは『処理の固まり』を作るのもリソース作成としてとらえることです(なんでもかんでもやると妙ちくりんですが)。

blogsのなかにネストさせるなど細かいやりようはいろいろあると思いますが、こんな形でいかがでしょうか。


編集前は'import'を勧めていましたが、考え方は変わらず、importは名詞でもあるので(ごさんこう: http://ejje.weblio.jp/content/import )、import(取り込み処理)というリソースを作成したという解釈でimportという名前でもいいかなと思ったのでした。

編集 履歴 (1)
ウォッチ

この質問への回答やコメントをメールでお知らせします。