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


「[《REST思想》と《リソース指向》と《Webページ》を一緒にしてはいけない](http://qiita.com/irxground/items/cd83786b10d81eecce77)」についてです。


用語の問題などについてはきっちり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では別ページにするのがより一般的という考えから、このデフォルトになっているというわけです。

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

> - 更新FormページのURLとPOST先のURLを同じにする
>
> （中略）
> URLを同じにすることで、CSSなどのリソースのパスや、リクエストパスに依存したコードを書きやすくなります。

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

## 問題だと思うこと

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

```ruby
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](https://www.google.co.jp/search?q=HATEOAS)の重要な要素であってRESTでも大切なのに、歴史的な事情か、仕様や実装に足りない部分がある気がするんですよね。

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

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

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