岩本隆史の日記帳(アーカイブ)

はてなダイアリーのサービス終了をうけて移行したものです。更新はしません。

HTTPの仕様からRESTfulなWebアプリのMVCを見直す

Smalltalk由来のMVCとWebアプリのMVC

最近、WebアプリにおけるMVCに関する記事をふたつ書きました。

  1. WebアプリでもSmalltalkのMVCパターンが使えるかも
  2. サーバサイドでSmalltalkのMVCパターンを使うのは無理があるかも

これらの記事で私が想定しているのは、GUIのフォームに何らかの文字列を入力してボタンを押すと、モデルの内容が変更され、その変更を感知したビューが自身を書きかえるという、Smalltalk由来のMVCです。

Webアプリのフレームワークで俗にMVCとよばれる実装は、Controllerのメインメソッドの中でModelを変更し、Viewのインスタンスを作って変更結果をassignする手法です。私はこれが嫌いで、Smalltalk由来のMVCをWebアプリで使いたくなったわけです。

このことは、ひとつめの記事にも書きました。

私は以前から、WebアプリのMVCは美しくないと思っていました。特に美しくないと感じるのは、モデルの処理に失敗したときにフォーム再表示用のビューを作ってエラーメッセージをassignするような場面です。SmalltalkMVCならば、処理が失敗したことをビューに通知し、ビューを更新すれば済みます。

WebアプリでもSmalltalkのMVCパターンが使えるかも

フォームを無思慮に再表示させていないか

しかし、あれこれ考えるうち、引用文の強調部分に罠があることに気づきました。しかもこれは、私だけではなくて、Webアプリ作成者のほとんどが陥っている罠だと思います。

罠とは、こういうことです。140文字までしか投稿できないマイクロブログがあるとしましょう。あなたがこのマイクロブログの作成者だとして、141文字以上の投稿がPOSTされたときの処理をどう実装しますか。おそらく、投稿フォームの近くに「140文字以内におさめてください」のようなメッセージが書かれたエラー画面を表示させる方が多いのではないでしょうか。

過去の私も、何の疑いもなく、そのように実装していただろうと思います。しかし、ある事実に気づいてしまいました。

なぜエラー時のレスポンスにフォームを含めるのか

その事実は、RFC 2616(HTTP 1.1の仕様が定められている)に書かれています。「10.4 Client Error 4xx」の一部を引用します。

ステータスコードの 4xx クラスは、クライアントが間違えているような場合を示す。 HEAD リクエストへのレスポンスを除き、サーバはエラー状況が一時的か恒久的かに拘わらず、エラー状況の説明を含むエンティティを返すべきである。

10.4 Client Error 4xx(ハイパーテキスト転送プロトコル -- HTTP/1.1)

先に想定した投稿エラーは、クライアントが間違っている(入力文字列が長すぎる)ために起こるものです。この場合、サーバは「400 Bad Request」など 4xx 系のステータスコードを返すことがHTTPの仕様上は望ましいでしょう。

そして、そのレスポンスエンティティには「エラー状況の説明を含む」「べきである」と書かれています。「エラーメッセージだけでなく、入力値が維持されたフォームを返すべき」などとは書かれていないのです。

入力値が維持されたフォームをサーバが返すとすれば、それは:

  1. 「入力値を修正すれば処理が正しく行える」とクライアントに伝えるべきだな
  2. ならば、入力値を修正しやすいようにレスポンスにフォームを含めてあげると親切かもしれないな

といった厚意によるものにすぎません。

POST前の画面が再現できればよいのか

「結果的に同じではないか」といわれるかもしれませんが、本当にそうでしょうか。先のエラー画面を、なるべくPOST前の画面に合わせようとしていませんか。より具体的にいえば、Webアプリのロゴや、サイドメニュー、広告などをレスポンスに含めていませんか、ということです。それが悪い習慣だとまではいいませんが、エラーと無関係なリンクや画像をレスポンスに含めても、クライアントの役に立たない可能性があることは心に留めておくべきなのではないでしょうか。

もっといえば、自作のWebアプリ以外のフォームから投稿されるかもしれないわけです。元の画面に合わせるなどというのはもとより不可能なのですから、エラー解消に役立つ最小限のレスポンスだけを返す方向性もありうるのではないでしょうか。

レスポンスをUIに合わせる必要はない

ここまで考えてくると、Smalltalk由来のMVCをWebアプリに応用するのは難しいことがわかります。Webアプリにおいては、送信前のフォームと送信後のフォームとは、まったく別物なのです。

いや、AjaxなどのRIAであれば、それなりに美しいMVCが実現可能でしょう。ただしその場合も、サーバの返すレスポンスをたとえばAjaxに特化する必要はないはずです。エラーの解消に役立つことを第一に考えれば、フォームを含むHTMLを返すのが最適でしょう。エラーメッセージだけ抽出して利用するかどうかは、クライアントの判断に任せるべきだと思います。