『Webを支える技術』読書記録 第5部


はじめに

今回『Webを支える技術』という本を読みましたので、自分なりにアウトプットしていく目的でまとめます。
リンク:『Webを支える技術――HTTP、URI、HTML、そしてREST』

第15章 読み取り専用のWebサービスの設計

リソース設計とは何か

 リソース設計とは、クライアントとサーバの間のインターフェースの設計、つまりWebサービスやWebAPIの外部設計のことである。リソース設計する上でWebサービスとWebAPIを分けて考えないことが重要である。

リソース思考アーキテクチャ

 リソース設計にはまだ、一般的な設計手法が存在しない。唯一指針として推奨されているのが書籍『RESTful Webサービス』が推奨している「リソース思考アーキテクチャ」の設計アプローチである。この指針においては、下記のようなステップで設計される。

1.Webサービスで提供するデータを特定する
2.データをリソースに分ける
3.リソースにURIで名前をつける
4.クライアントに提供するリソースの表現を設計する
5.リンクとフォームを利用してリソース同士を結びつける
6.イベントの標準的なコースを検討する
7.エラーについて検討する

郵便番号検索サービスの設計

 リソース設計の例として以下のような郵便番号検索サービスを設計する。

  • 日本の郵便番号情報を提供する
    • 郵便番号情報は、郵便番号、住所、住所のよみから成る
    • 住所は、都道府県名、市区町村名、町域名から成る
  • 郵便番号の前方一致で、郵便番号情報を検索出来る。
  • 住所及び、住所の読みで、郵便番号情報を全文検索出来る。
  • データはすべて読み取り専用である。

Webサービスで提供するデータを特定する

 リソース設計の最初の工程は、サービスで提供するデータを理解し特定する作業である。このサービスで提供するデータは、日本郵便が提供している郵便番号のCSVデータをもとにしている。以下3つのデータである。

  • 7桁の郵便番号
  • その郵便番号が表現する住所(都道府県名、市町村名、町域名)
  • 住所のカタカナ読み

データをリソースに分ける

 この工程は難解かつ重要である。このサービスには郵便番号や住所をキーワード検索する機能を付与される。機能をリソースとして落とし込む場合は、機能の結果をリソースとして捉えることが重要である。また、クライアントがこのサービスを利用する時に、最初にサクセスするトップページのリソースが必要となる。このサービスで提供するリソースは以下である。

  • 郵便番号リソース
     1つの郵便番号に対応するリソース、その郵便番号の住所やよみも入っている。

  • 検索結果リソース
     郵便番号の一部や住所の一部で郵便番号を検索した結果のリソース

  • トップレベルリソース
     このWebサービスのスタート地点。都道府県リソースへのリンクと、検索フォームを含んでいる。

リソースにURIで名前をつける

 リソースの用途に応じて、URIを検討する必要がある。ここで紹介されるサービスの各リソースに対する名前は以下のようになっている。

  • 郵便番号リソース  郵便番号によって、一意に識別する方法を取っている。例は以下である。

例:http://zip.ricollab.jp/1120002

 ハイフンを入れて表現することも出来るが、プログラム向けのAPIがサンプルとなっているため、よりプログラムから扱いやすい数字のみの郵便番号が選択されている。

  • 検索結果リソース  検索キーワードの取得を行うため、クエリパラメータを使用する。例は以下である。

例:http://zip.ricollab.jp/search?q=小石川

 /serachは検索クエリを受け取るリソースのURIである。このURIには検索クエリを表現する「q」というクエリパラメータが必ず付く。実際には漢字表記はUTF-8で%エンコードされる。また、一部がパラメータとして変動するURIを記述する場合は、「{」と「}」でパラメータ部分を囲む「URI Templates」という表記が一般的である。

  • 地域リソース 地域リソースは階層を持ち、都道府県名の下には市区町村が、市区町村の下には町域が存在する。階層構造をURIで表現する場合は「/」を使用する。例は以下である。

例:http://zip.ricollab.jp/{都道府県名}/{市区町村名}/{町域名}

  • トップレベルリソース  サンプルとなっているサービスのスタート地点となるリソースである。このようなリソースには通常一番ルートとなるURIを付与する。

例:http://zip.ricollab.jp

クライアントに提供するリソース表現の設計

 各リソースの表現形式はXMLと軽量フォーマットの表現が採用されている。以下に、現在Web上の代表的な表現形式を記す。

  • XML表現
    • XHTML
    • Atom
    • 独自XML
  • 軽量フォーマット表現
    • JSON/JSONP
    • YAML
    • CSV
  • マルチメディア表現
    • 画像(GIF、JPEG、PNG)
    • 映像(MPEG、WMV、MOV)
    • マルチページ画像(PDF、TIFF)

XML表現

 XML表現を使用するに当たって注意するべき事は、「独自フォーマットを作り出さないこと」である。XMLの利点は既存のフォーマットに不足があれば、あとからタグを付けて拡張できる点にあるためである。XMLの既存フォーマットはXHTML、Atomである。このサンプルサービスでは、郵便番号にはAtomで必須の著者や更新日時がない点や、ブラウザで表示するフォーマットが必要である点から、XHTMLが採用されている。

軽量フォーマット表現

 軽量フォーマットの代表的なフォーマットとしては、上記の通り、JSON、YAML、CSVである。このサンプルアプリではJSONが採用されている。各フォーマットの利点と欠点に関しては以下である。

  • JSON
    • 利点:JavaScriptとの相性が良い。配列やハッシュタグなどのデータ構造が存在する。文字エンコーディングはUTF-8、UTF-16、UTF-32のいずれかに固定される
    • 欠点:YAMLと比較して書きづらい。
  • YAML
    • 利点:読みやすく、書きやすい。
    • 欠点:ライブラリが比較的少ない
  • CSV
    • 利点:表形式のデータとの相性が良い。読み込み可能なソフトが多数存在する。
    • 欠点:エスケープ文字の問題など、文字コードの扱いが難しい。

URIで表現を指定する

 このサンプルアプリではXHTMLとJSONが採用されている。リソースの表現はAcceptヘッダだけでなく、URIでも指定できるよう設定が可能である。まず、例として、郵便番号リソース、地域リソースでは、XHTML表現には「.html」、JSON表現には「.json」という拡張子をつける。

例:http://zip.ricollab.jp/1120002.html
例:http://zip.ricollab.jp/東京都/文京区/小石川.json

JSONPでコールバック関数名を渡す場合は以下である。

例:http://zip.ricollab.jp/1120002.json?callback={コールバック関数名}

 検索結果リソースは、クエリパラメータを受け取り結果を返すリソースであるため、このクエリパラメータで表現の指定を行う。例は以下である。

例:http://zip.ricollab.jp/search?q=小石川&type=json&callback={コールバック関数名}

リンクとフォームを利用しリソース同士を結びつける。

 設計したリソース同士はリンクで接続する必要がある。通常のWebページでは、トップページへのリンク、パンくずリスト、グローバルナビゲーションなどを用意して、クライアントを他のリソースへと導くよう促すことが多い。

 以下では検索結果リソースを例に挙げ、XHTML表現とJSON表現の書き方を示す。

result.html
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>「112」の検索結果</title>
  </head>
<body>
  <h1><span class="query">112</span>」の検索結果</h1>
  <p><span class="totalResults"
       >101</span>件中1件目から<span class="itemPerPage"
      >10</span></p>
<ul class="zipcode">1120000</span>
    <li>
      <span class="zipcode">1120000</span>
      <a href="http://zip.ricollab.jp/1120000"
        class="address">東京都文京区以下に掲載がない場合</a>
    </li>
   <li>
      <span class="zipcode">1120001</span>
      <a href="http://zip.ricollab.jp/1120001"
        class="address">東京都文京区白山(2~5丁目)</a>
    </li>
・・・
  <li>
      <span class="zipcode">1120013</span>
      <a href="http://zip.ricollab.jp/1120013"
        class="address">東京都文京区音羽</a>
    </li>
  </ul>
  <p><a href="http://zip.ricollab.jp/search?q=112&amp;page=2"
    rel="next">次へ</a></p>
</body>
</html>
result.json
{
  "query": "112",
  "totalResults": 101,
  "itemPerPage": 10,
  "next": "http://zip.ricollab.jp/search?q=112&amp;page=2",
  "result": [{
    "zipcode": "1120000"
    "address": "東京都文京区以下に掲載がない場合",
    "link": "http://zip.ricollab.jp/1120000"
 }
 {
  "ziocode": "1120001"
  "address": "東京都文京区白山(2~5丁目)",
  "link": "http://zip.ricollab.jp/1120001"
 },
・・・
 {
  "zipcode": "1120013",
  "address": "東京都文京区音羽",
  "link": "http://zip.ricollab.jp/1120013"
 }]
}

 XHTML表現では、郵便番号リソースへのリンクと「次へ」の検索結果リソースへのリンクが記載されている。JSON表現では、「次へ」のリンクに代わりnextというメンバが追加されている。また、各検索結果にはlinkメンバがあり、郵便番号情報にリンクしている。

リソース間のリンク関係

 下の図は、サンプルアプリにおける各リソースのリンク関係である。このように図に起こすことで、孤立しているリソースやリンクすべきリソースがリンクしていない、などの問題を把握しやすくなる。

イベントの標準的なコースの検討

 サンプルとなっているサービスの標準的な利用コースは以下の様に想定される。

  • 郵便番号の検索

    • フォームに郵便番号を入力
    • 検索結果リソースを取得
    • 目的の郵便番号リソースを取得
  • 住所から郵便番号を検索

    • フォームに住所を入力
    • 検索結果リソースを取得
    • 目的の郵便番号リソースを取得
  • 地域リソースの階層を辿りながら郵便番号を検索する。

    • 都道府県リソースを選択し、市区町村一覧を表示
    • 市区町村リソースを選択し、町域一覧を表示
    • 町域リソースを選択し、郵便番号一覧を表示
    • 目的の郵便番号リソースを取得

このようにWebサービスのリソースを設計する際には、クライアントの挙動のフローについて検討する必要がある。

エラーについての検討

 どのようなエラーが起こりうるかについても検討する必要がある。このサンプルサービスでは以下の様なエラーが想定される。

  • 存在しないURIを指定した
  • 必須パラメータを指定していない
  • サポートしていないメソッドを使用した

リソース設計はスキルであり、身につけることが出来るものであるので、より良い設計が出来るよう研鑽することが肝要である。

第16章 書き込み可能なWebサービスの設計

 読み取り専用のWebサービスに比べ、書き込み可能なWebサービスは懸案事項が多数ある。例えば、複数ユーザーの同時の書き込みの想定や、複数の処理手順を必ず実行するための方法の検討などである。実際に読み取り専用の検索サービスに、書き込み機能を追加する形で解説する。追加する機能は以下である。

  • リソースの作成
  • リソースの更新
  • リソースの削除
  • バッチ処理
  • トランザクション
  • 排他制御

リソースの作成

 リソースの作成方法は2つあり、1つはファクトリリソースへPOSTする方法で、もう1つはPUTで直接作成する方法である。

  • ファクトリリソースによる作成

 ファクトリリソースとはリソースを作成するための特別なリソースである。ファクトリリソース自体はWebサービスによって予め用意し、POSTで新しいリソースを作成する。

  • PUTによる作成

 リソースのURIが予め決められている場合は、PUTでも作成することができる。この場合、新しく作成したいリソースのURIにリクエストを直接送る。作成するリソースのURIをクライアントが既に知っているため、レスポンスメッセージにLocationヘッダは含めない。

 一般的にはPOSTでリソースを作成するが、サーバ側の実装が簡単になる点や、クライアントが作成と更新を区別しなくてよくなるので、クライアント側の実装が簡単になる点などから、PUTによって作成する場合もある。しかし、クライアントがURI構造を把握しなければならない点や、リクエストを見た時に、その操作が作成なのか更新なのか区別が難しいなどの欠点もある。

リソースの更新

 リソースの更新は基本的にPUTで行う。バッチ更新に関しては、POSTで行われる。

  • バルクアップデート

 PUTの最も基本的な使い方は、更新したいリソース全体をそのままメッセージボディに入れる方法である。リソース全体を送信する更新方法をバルクアップデートと呼ぶ。バルクアップデートはクライアントの実装が簡単になる反面、送受信するデータが大きくなるという欠点がある。

  • パーシャルアップデート

 バルクアップデートとは異なり、部分的なデータを送信するパーシャルアップデートという方法がある。パーシャルアップデートは送受信するデータが少なくなる反面、GETしたリソースの一部を修正し、PUTするという使い方が不可能である。サーバ側でパーシャルアップデートをサポートする場合、通常はバルクアップデートもサポートすることが推奨される。

 また、更新できないプロパティ(このサンプルサービスでは「郵便番号」など)を更新しようとした時は400 Bad Requestなどのエラーを返し、更新できないことをクライアントに伝えるなどの処理が必要になる。

リソースの削除

 リソースの削除は、削除したいリソースのURIにDELETEを送信して行う。このときの留意点としては、削除対象のリソースの配下に子リソースが存在する場合は、子リソースも親リソースの削除に伴って削除する必要があることである。

バッチ処理

 大量のデータを作成したり、更新したりする場合は、リクエストをその度に送信すると、サーバへの接続回数が多くなり、パフォーマンスに問題が発生する可能性がある。このような場合は、作成、または更新したいリソースを一括で送信出来るよう、Webサービスを実装する。この際、レスポンスでエラーが起きた場合の対処は2通りある。

 1つはバッチ処理をトランザクション化して、途中で失敗した場合は何も処理しないことをWebサービスの内部で保証することである。もう1つはどのリソースの処理が成功して、どのリソースへの処理が失敗したのかをクライアントに伝えることである。伝え方には207 Multi-StatusとWebDAVの要素を組み合わせる方法と、200 OKと独自のフォーマットを組み合わせる方法の2つがある。

トランザクション

 実際のシステムでは、複数のリソースにまたがった変更をひとまとまりに扱う、いわゆるトランザクションが必要になる場合も存在する。例えば、銀行口座の振込などでトランザクションを用いない場合、振込元の口座の残高の処理と振込先の口座の残高の処理がどちらか成功し、どちらかが失敗する、といったことが発生してしまう。このような時、失敗した場合はどちらの処理ももとに戻すことを保証するのがトランザクションである。

 トランザクションをRESTfulに実現するために重要となるのがトランザクションリソースである。HTTPの統一インターフェースで実現できない処理があった場合には、新たなリソース(トランザクションリソース)を導入し解決を図るのがRESTfulな設計の定石である。

トランザクションの流れ

 まず、利用したいリソースにPOSTを送信して、トランザクションを開始する。トランザクションリソースが作成出来たら、使用するリソースのURIを追加する。その後、トランザクションを実行する。成功したら、このトランザクションリソースを削除して完了である。

排他制御

 通常のWebサービスでは複数のクライアントを想定する必要がある。そのときに必要になってくるのが排他制御である。排他制御とは、複数のクライアントが同時に1つのリソースを編集して競合、いわゆるコンフリクトが起きないよう、1つのクライアントのみが編集するよう制御する処理のことである。バッチ処理やトランザクションは、通常は排他制御を行いながら実行する。
 排他制御の方法は大きく分けて2つあり、1つは悲観的ロック、もう1つは楽観的ロックである。

  • 悲観的ロック

    • リソース編集の競合を防ぐため、ロックの権限を持った人以外が編集できないような制限をかける方法である。HTTPで悲観的ロックを実現するためには、WebDAVとLOCK/UNLOCKメソッドを使用する方法と、独自のロックリソースを使用する方法がある。
  • 楽観的ロック

    • 通常の編集では文書をロックせず、競合が起きた時に対処する仕組みである。HTTPで楽観的ロックを実装するには条件付きPUTと条件付きDELETEを利用する。

設計のバランス

 リソース設計の指針としては以下3つが挙げられる。

  • なるべくシンプルに保つ
    • 設計が複雑化した場合は、メタ的な視点から検討し、不能な機能等を洗い出してシンプルに保つ必要がある。
  • 困ったらリソースに戻って考える
    • HTTPメソッドで実現できない機能がある場合は、独立した別リソースで代替できないか考える。
  • 本当に困ったらPOSTで何でも出来る
    • 更新にはPUTが推奨されるがバッチ処理など複数のリソースを対象とする時などは、POSTを用いるほうが良い場合もある。

第17章 リソースの設計

リソースの設計

 リソースの設計は未だ定石が存在せず、何から手を付けるべきかわからなくなることも多い。リソース思考アーキテクチャの設計アプローチでは「Webサービスで提供するデータの特定」や「データをリソースに分ける」方法が確立されていない。そのような中でもある程度確立されている既存の設計手法で得られた成果物をもとにリソースを設計する方法がある。その成果物とは以下である。

  • 関係モデルのER図
  • オブジェクト指向モデルのクラス図
  • 情報アーキテクチャ

リソース設計の重要事項

 リソースを設計するときにはWebサービスとWebAPIを分けて考えないことが非常に重要である。WebサービスとWebAPI、主にクライアントが人間であるか、プログラムであるかという用途は異なるが、両者に大きな違いは無い。WebAPI用として作ったリソースがWebサービスで使用出来ることも多い。WebサービスとWebAPIを分けて考えずにリソース設計することが肝要である。

参考書籍詳細

WEB+DB PRESS plusシリーズ『Webを支える技術――HTTP、URI、HTML、そしてREST』
山本陽平著 技術評論社 2010年5月1日 p.242-310