LoginSignup
156
152

More than 5 years have passed since last update.

《REST思想》と《リソース指向》と《Webページ》に関する(主にRailsの)話

Last updated at Posted at 2014-03-21

《REST思想》と《リソース指向》と《Webページ》を一緒にしてはいけない」についてです。

用語の問題などについてはきっちりJxckさんが指摘されているので、私は自分で復習しつつ思ったことを書いてみます。

なんかRESTって難しい…とはあんまり思ってほしくないんですが、少なくともRails使うとちゃんとしたことが楽にできますよ。

REST思想

本に対してCRUD操作を行うインタフェースとして、以下のようなものを提供します。

役割 method + URL
本一覧を取得 GET /books
一つの本を取得 GET /books/1
新しい本を追加 POST /books
既存の本を修正 PUT /books/1
既存の本を削除 DELETE /books/1

これはいわゆるWeb APIについて、ということかなと推測しました。RESTというのはAPIのプロトコルのことだと思われている傾向がありますが、そういうわけではありません。Web全体についてのもので、APIについてもWebアプリについても適用されるものです。

実はRESTでは「統一インターフェイス」の制約からメソッドについて規定されていますが、URLの形については特に規定されません(もちろんAddressabilityの面で重要であることは言うまでもありません)。なので実は/books,/books/1でなくてもいいのですが、これを規約(CoC)でズバッときれいに決めてしまったのがRailsのすごいところの1つです。

本の追加や削除を行う場合は、本情報をJSON形式でPOSTリクエストのボディとして送ります。application/x-www-form-urlencoded形式で送ることは避けるべきです。
(中略)
しかし、正常系・異常系ともにアプリケーション内で統一された形式を利用してください。

このへんは、表現のフォーマット(メディアタイプ)の話なので直接RESTと関係するわけではありません。対応できるなら複数のフォーマットに対応すると便利だけど、実装の手間を考えて1つのフォーマットに絞る、というのは十分ありえます。

Railsの場合、デフォルトでapplication/x-www-form-urlencodedもJSONも扱えるので、実はそんなに手間でもありません。

リソース指向

本の情報は/booksにあるとして、拡張子を変えることで、様々なフォーマットでデータを利用できるようにします。
例えば本の一覧をJSON形式で欲しければ/books.jsonにリクエストし、csv形式で欲しければ、/books.csvにリクエストします。
/books.xlsのようにバイナリを提供してもかまいません。

これこそリソースと表現(Representation)の違いですね。
「リソース」はURLで表されますが、抽象的な概念であって、データではないので、送ることができません。送られるのはすべて表現です。(すべてのデータは表現、と言い換えてもよい)
表現にどのフォーマットを使用しているかを表すのがHTTPのContent-Typeヘッダで、どのフォーマットなら受け入れられると伝えるのがAcceptヘッダです。

GET /books HTTP/1.1
Host: example.jp
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json

[
  {"name": "foobar"},
  ...

GET /books HTTP/1.1
Host: example.jp
Accept: text/html
HTTP/1.1 200 OK
Content-Type: text/html

<!DOCTYPE html>
<html>
  <head>
  ...

HTTPにはフォーマットを使い分ける機能がすでにあったんですね。
ブラウザで試すときなど、URLで表したほうが便利なこともあるので、/books.jsonなどの拡張子的なURLもよく使われます。(Railsではデフォルトでどちらも対応)

ただし、たとえば.jsonであってもapplication/jsonとは限らず、application/ld+jsonとかapplication/hal+jsonとかほかのタイプもありうるので、拡張子の指定よりはAccept,Content-Typeの指定のほうが正確といえるでしょう。

ということで、

/books.csvに対する更新を行う場合は、はPOSTリクエストのボディとして何を送ればよいのでしょうか。CSV形式で良いのでしょうか。JSONでしょうか。
また、更新に失敗したときのエラーはどう返すべきでしょうか。もし/books.csvにリクエストししたにも関わらず、JSON形式でエラーを返ってくることになると、クライアントは苦労するでしょう。

このへんの心配は、ちゃんと実装すれば杞憂ですし、Railsなら実装も比較的簡単です。

Webページ

(中略)

役割 URL
一覧画面を表示 GET /books
詳細画面を表示 GET /books/1
追加formを表示 GET /books/new
追加アクション† POST /books/new
編集formを表示 GET /books/1/edit
編集アクション† POST /books/1/edit
削除確認画面を表示(任意)† GET /books/1/delete
削除アクション† POST /books/1/delete

(引用注:†はRailsのデフォルトにはありません)

Railsを使いはじめるとこのnewとかeditってのは何なんだ、と思ってしまうのですが、これも別に必須というわけではありません。例えば、一覧画面の中に追加フォームを入れてしまうというのは、よく見かけますね。
どちらがいいかは場合によるのですが、Railsでは別ページにするのがより一般的という考えから、このデフォルトになっているというわけです。

ある意味neweditは、表現の種類の1つと考えられるかもしれません。

  • 更新FormページのURLとPOST先のURLを同じにする

(中略)
URLを同じにすることで、CSSなどのリソースのパスや、リクエストパスに依存したコードを書きやすくなります。

相対パスを書くと/books/books/newの違いでハマることがある、ということですね。これは少し経験があるけど、全体としては些細なことです。というかRailsなら相対パスを使わずに済みます。

問題だと思うこと

Railsのコントローラの、ほぼscaffoldのコードはこんな感じです。

def create
  @book = Book.new(book_params)

  respond_to do |format|
    if @book.save
      format.html {
        # 302 Found (or 303 See Other)
        redirect_to @book, notice: 'Book was successfully created.'
      }
      format.json {
        # 201 Created
        render action: 'show', status: :created, location: @book
      }
    else
      ...
    end
  end
end

respond_toを使うと、ロジック部分をそのままにフォーマットの違いに対応できる!すごい!
というのは確かなんですが、なんでフォーマットによってステータスコードが違うの?

新規作成したら、ほんとは201 Createdを返したいんですが、ブラウザは201を受け取ると自動的にはリダイレクトしない。そこさえ同じなら、respond_toすら書かなくてよくなるのに。
Webブラウザで必要とされる画面遷移が特殊なのかもしれないですが、画面遷移(状態遷移)こそHATEOASの重要な要素であってRESTでも大切なのに、歴史的な事情か、仕様や実装に足りない部分がある気がするんですよね。

そういう意味では、XHR(Ajax)のブレイクスルーはかなり大きかったですね。PUT/DELETEも可能になったり、ブラウザの機能すら補ってくれた。昔「Web ApplicationをRESTfulに近づけていくことで、Web Serviceとの垣根をなくす方向を目指したい」と言っていたのを思い出して懐かしい気分になりました。

読書会とミートアップのお知らせ

RESTful Web APIs』という書籍の読書会を毎月第2・4木曜日に行っています。これはRailsや特定の言語にまったく依存しない内容です。RESTやWeb APIに興味のある方の参加をお待ちしています。
http://www.circleaf.com/groups/19

その著者のMike Amundsen氏の来日にあわせて、ミートアップを4/12 19:00から渋谷で開催します。こちらもよろしくお願いします。
http://sendagayarb.doorkeeper.jp/events/10103

156
152
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
156
152