2
内容 (1)n はじめにn 想定する読者n RESTful API 概説
– REST とは– RESTful API とは– XML か JSON か
n RESTful API 設計– API エンドポイント設計
• API エンドポイント設計の基本u Resource Chaining
• API エンドポイント設計のポイント• HTTP メソッドとリソースに対する操作
• コレクション・リソースの取得u 全件リストから⼀部を取り出すu 検索フィルタを⽤いた結果セットの絞り込みu パスとクエリ・パラメータの使い分け
– API レスポンス設計• メディア・タイプの指定• 応答コードの指定
u 応答コード 200番台の使い分けの例
• レスポンス・データのフォーマットu レスポンス・データ名の検討 - 性別と⽇付
• エラーの表現• Same-Origin Policy と CORS• API 応答のキャッシュの制御• ⼤量呼び出しへの対策
3
内容 (2)
– API へのリクエスト• Server Driven Content Negotiation
– ⽇本語固有の話題– API 運⽤
• API 設計・実装の変更とユーザーへの通知の必要性u API 設計変更の通知⽅法
u バージョン番号をどう付けるか
• API 公開の終了
n [参考] Swagger
n RESTful API 実装 〜 JavaScript 編n RESTful API 実装 〜 Java 編n 参考図書・参考⽂献
4
はじめに
n この資料は RESTful API の設計・実装に初めて携わる⽅を対象に、その基本的な事柄を中⼼に解説したものです。
n 現在のところ、RESTful API 設計の規則や規約の唯⼀のものが存在するわけではなく、各種のプラクティスが各所から発⾏されている状態です。– この資料に記載されている内容も、別の資料では異なる記述となっていることがあり得ます。– この資料の作成に際しては複数の既存の資料を検討し、最も無理なく⾃然に理解できる平易
な記述となるように努めています。n RESTful API 実装の解説では例として、JavaScript と Java を⽤いた
Cloudant NoSQL DB に対する CRUD 操作の RESTful API 化を⾏っています。– RESTful API 設計に記した事柄を網羅的に取り⼊れることはしていません。
• この資料を利⽤される⽅の要件に応じて、実装を追加するようにして下さい。
5
おことわり
nRESTful API 実装の節に⽰すサンプル・コードは完全に無保証であり、現状有姿で提供されるものです。– あくまで RESTful API 実装の基本を理解するためのサンプルであり、実働⽤コードは
各プロジェクトでの要件に従って実装する必要があります。– 掲載のコードを使⽤したことによるいかなる結果にも、発⾏者は責任を負いかねます。
6
想定する読者
nRESTful API 設計・実装に初めて携わる⽅。
nRESTful API 設計担当者– HTTP の概要を理解していること。
nRESTful API 実装担当者– Java 開発者
• Java プログラミングの経験があること。u Java EE サーバー・サイド (Web) アプリケーション開発を経験しているとなおよい。
– JavaScript 開発者• JavaScript プログラミングの経験があること。
u Express.js での Web アプリケーション開発を経験しているとなおよい。
8
REST とは
nREpresentational State Transfer– Roy T. Fielding ⽒の博⼠論⽂ (2000年) が初出とされています。
• アーキテクチャ原則と制約– 詳しくは原⽂をご参照下さい。
• Architectural Styles and the Design of Network-based Software Architectures -CHAPTER 5 Representational State Transfer (REST)u http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm
9
RESTful API とは
n現在では、Fielding ⽒の原則と制約の厳密な実装ではない Web サービスも⽤いられています。– “RESTful API” と呼ばれることがあります。
nおおよそ、以下のような特徴を持ちます。– 転送プロトコルに HTTP を⽤いる– URI で対象を⽰す– HTTP メソッドで操作を⽰す
nこの資料では、上の RESTful API について扱うものとします。– 単に API と記述することもあります。
10
XML か JSON か
nどのようなデータ・フォーマットを⽤いるかを検討します。– 現状の RESTful Web サービスでは XML か JSON のほぼ⼆択です。– 傾向としては XML がやや衰退し、JSON が増えています (2017/02 現在)。
• https://www.google.co.jp/trends/explore?q=XML%20API,JSON%20API
nこの資料では、データ・フォーマットは JSON を前提とします。nXML を併せてサポートすることで、ユーザー (API を利⽤するアプリ
ケーション開発者) の獲得に寄与するかもしれません。
12
API エンドポイントの基本
n<スキーム>://<ホスト名>[:ポート番号]/<パス> の形式です。– 例) http://api.example.com/users/123
nパスに動詞を含めないようにします。– 動詞 (操作) は HTTP メソッドで表現します。
nインスタンス・リソースのエンドポイント– パスは /<項⽬>/<ID> の形式です。
• ○ http://api.example.com/users/123
nコレクション・リソースのエンドポイント– パスは /<項⽬> の形式です。
• 例) http://api.example.com/users
13
Resource Chaining
nリソースの階層を表現します。– http://api.example.com/users/123/orders/456/items/789
nインスタンス・リソースとコレクション・リソースも適⽤できます。– インスタンス・リソース
• http://api.example.com/users/123/orders/456/items/789– コレクション・リソース
• http://api.example.com/users/123/orders/456/items/
14
API エンドポイント設計のポイント (1)
n短く⼊⼒しやすいことを⼼がけます。– 充分な情報を含んだ上で、⻑いより短い⽅が間違いを減らすことができます。– 過度な省略は誤解を招くことがあるため注意します。– 省略形やコードなどを⽤いる場合は、規格があれば従います。
• 銀⾏コード (統⼀⾦融機関コード): 全国銀⾏協会• 国名コード: ISO 3166-1 / JIS X 0304 など• 空港コード: 国際航空運送協会 (IATA), 国際⺠間航空機関 (ICAO) など
n例– × http://api.example.com/web-services/api/users/123– ○ http://api.exmple.com/users/123
15
API エンドポイント設計のポイント (2)
n⼈間が読んで理解できるようにします。– 理解できる単語で構成されたエンドポイントは、API の「意図」を理解しやすくなり
ます。• 意図を理解しやすい API は、アプリケーションから利⽤しやすくなります。
– 標準的な英語の複数形を使⽤します。• 例
u × seihinu × purodakutou × produ ○ products
• 迷った時には、辞書を引いて確認します。• 既存の API を調べることも参考になります。
u ProgrammableWeb (https://www.programmableweb.com/) などで、多くの API を参照できます。
16
API エンドポイント設計のポイント (3)
n⼀般的すぎる単語を避けます。– API の意図が理解しにくくなります。– 特に、以下を⼀語で使⽤する場合は、それが本当に必要か充分に検討します。
• Element• Entry• Instance• Item• Object• Resource• Type• Value
17
API エンドポイント設計のポイント (4)
n⼀つの単語の場合は⼩⽂字を⽤います。– HTTP サーバーや OS の実装によって、⼤⽂字/⼩⽂字が区別されるかは異なります。– スキームとホスト名は⼤⽂字/⼩⽂字を区別しません。
• ⼀般には⼩⽂字で表記されます。– スキームに続くパスも⼩⽂字を⽤いることが⾃然です。
n単語のつなげ⽅に注意します。– 以下を含む⽅法が⽤いられています。
• “-” (ハイフン) でつなぐ• “_” (アンダースコア) でつなぐ• キャメル・ケース (例: camelCase) でつなぐ
– 現在のところ、いずれかを選択する特段の強い理由はなく、いずれでも構いません。• ただし、どの⽅法を使⽤するか統⼀することが重要です。
18
API エンドポイント設計のポイント (5)
n実装を反映しないようにします。– 実装は API ユーザー (アプリケーション開発者) にとっては無⽤の情報です。– インターフェースに実装が反映されていると、実装を制約したり、実装に際して誤解
を招くことがあります。– 不⽤意に実装を推測させることは、攻撃に対する脆弱性につながることがあります。
• × http://api.example.com/cgi-bin/users/123u 「CGI で実装すべき」と解釈される可能性があります。u CGI で実装されていることが推測できます。
• × http://api.example.com/servlets/users/123u 「Java サーブレットで実装すべき」と解釈される可能性があります。u Java サーブレットで実装されていることが推測できます。
19
API エンドポイント設計のポイント (6)nパーセント・エンコーディングを要する⽂字を使わないようにします。
– URI に使⽤可能な⽂字は、以下の 3種類に分けることができます。1. 未予約⽂字 (RFC 3986 2.3. Unreserved Characters)
u ⽤途が予約されていない、URI 内で利⽤可能な⽂字で、パーセント・エンコーディングが発⽣しないものです。u アルファベット⼤⼩⽂字、数字と -._~ の 4⽂字です。
2. 予約⽂字 (RFC 3986 2.2. Reserved Characters)u URL ⽂字列内で区切りとして使⽤されるため、(パーセント・エンコーディングなしに) 区切り以外での使⽤を
してはいけません。u :/?#[]@!$&ʼ()*+,;= の 18⽂字です。
3. 予約⽂字でも未予約⽂字でもない⽂字u ⽇本語などのマルチバイト⽂字u スペース
n以下の場合にパーセント・エンコーディングが発⽣します。2. を区切り以外に使⽤する場合3. を使⽤する場合
20
HTTP メソッドのセーフと冪等
nセーフ– リクエストによってリソースの状態を変更しないことを指します。– GET と HEAD はセーフでなくてはなりません。
• RFC 7231 - 4.2.1. Safe Methods
n冪等(べきとう)– ⼀度のリクエストの結果と、そのリクエストの複数回の結果が等しいことを指します。
• セーフなメソッドは冪等です。• 結果がエラーや不整合であっても、複数回の結果が等しくなくてはなりません。
– GET, HEAD, PUT, DELETE は冪等でなくてはなりません。• RFC 7231 - 4.2.2. Idempotent Methods
– PATCH はセーフでも冪等でもありません。• RFC 5789 - 2. The PATCH Method
21
HTTP メソッドとリソースに対する操作
n⼀般に、以下の HTTP メソッドが利⽤されます。
HTTP メソッド リソースに対する操作 セーフ 冪等
GET リソースの取得 ○ ○
POST リソースの新規作成 × ×
PUT リソースの全属性の更新 × ○
DELETE リソースの削除 × ○
PATCH リソースの⼀部属性の更新 × ×
HEAD リソースのメタ情報の取得 ○ ○
22
HTTP メソッドとリソースに対する操作例 (1)
nGET http://api.example.com/users/1– ID が 1 のユーザーの属性を取得します。
nPOST http://api.example.com/users/1– ID が 1 のユーザーを作成します。– ユーザーの属性はリクエスト・ボディに記述します。
nPUT http://api.example.com/users/1– ID が 1 のユーザーのすべての属性を更新します。– 更新する属性はリクエスト・ボディに記述します。
nDELETE http://api.example.com/users/1– ID が 1 のユーザーを削除します。
23
HTTP メソッドとリソースに対する操作例 (2)
nPATCH http://api.example.com/users/1– ID が 1 のユーザーの⼀部の属性を更新します。– 更新する属性はリクエスト・ボディに記述します。
nHEAD http://api.example.com/users/1– GET と同様ですが、レスポンス・ボディを返さずにヘッダのみを返します。
24
カスタム・メソッド
nHTTP メソッドでは表現しにくい操作があります。– 仮想マシンの再起動– メールの送信– ...
nカスタム・メソッドを使⽤する⽅法があります。– http://api.example.com/users/1:sendMail
nカスタム・メソッドを使⽤する場合、HTTP メソッドは POST の使⽤が容易です。– 安全性と冪等性の制約が最も緩いためです。– リクエスト・ボディを送信することもできます。– 他のメソッドを使⽤する場合は、安全性と冪等性の制約に注意します。
25
コレクション・リソースの取得
n(再掲) コレクション・リソースのエンドポイント– パスは /<項⽬> の形式でした。
n例えばユーザー数が 1万⼈の場合、全件が⼀度に返されたとしたら…?– http://api.example.com/users
n何らかの⽅法で、結果セットの絞り込みを⾏う必要があります。
26
全件リストから⼀部を取り出す
n相対位置によりデータを取得する⽅法があります。– 1ページあたりの取得⼈数を決めて、ページングします。
• ID 1001 番から 10 ⼈を取得する。u http://api.exapmle.com/users?limit=10&offset=1000
• データ・ストアによっては、リストの後ろの⽅を取り出そうとするとパフォーマンスが低下することがあります。
n絶対位置によりデータを取得する⽅法があります。– 特定の ID や作成時刻などの前、または後のデータのみ取得します。
• ID が1000番と等しいか⼩さいユーザーを取得する。u http://api.example.com/users?max_id=1000
• 作成時刻が UTC で2016年1⽉1⽇ 00:00 以降のユーザーを取得する。u http://api.example.com/users?created-after=2016-01-01T00:00:00+00:00
27
検索フィルタを⽤いた結果セットの絞り込み
nデータの特定の属性を指定した絞り込みを⾏います。– name が babatch であるユーザーの検索
• http://api.example.com/users?name=babatch
n全⽂検索による絞り込みを⾏います。– いずれかの属性に “API” を含むユーザーの検索
• http://api.example.com/users?q=API
nいずれの場合も、API 実装では SQL インジェクションに注意します。– RDB の場合、プリペアド・ステートメントなど、プラットフォームが提供するバイン
ディング・メカニズムを使⽤する⽅法があります。– NoSQL データ・ストアであっても、SQL インジェクションと無縁ではありません。
• パラメータのチェックを⾏うなどし、意図しない検索が実⾏されないようにします。
28
パスとクエリ・パラメータの使い分け
n「⼀意なリソースを表すための情報か」で使い分けるとよいでしょう。– Yes: パス
• そもそも URI はリソースを表現するものです。– No: クエリ・パラメータ
• 検索フィルタの条件、アクセス・トークンなど、リソースを表現しないものはクエリ・パラメータとして送信します。
30
メディア・タイプの指定
nContent-Type ヘッダで以下のように指定します。– JSON - Content-Type: application/json– XML - Content-Type: application/xml
n IBM API Connect では API 定義内で設定することが可能です。
n正しく指定しない場合、セキュリティ上の問題となることがあります。– レスポンスに意図しない JavaScript コードが含まれている場合、ブラウザで
text/html と判断されればコードが実⾏されてしまいます。
31
応答コードの指定
nAPI レスポンスの応答コードを正しく設定します。– 呼び出し側で API の処理結果を知るための重要な情報です。
n⼀般的な HTTP 応答コードの使⽤⽅法に倣います。– 100番台: 情報– 200番台: 成功– 300番台: リダイレクト– 400番台: クライアント側 (リクエスト) に起因するエラー– 500番台: サーバー側に起因するエラー
32
応答コード 200番台の使い分けの例
n様々な議論がありますが、⼀例を挙げます。– GET
• 200 OK– POST
• 201 Created– PUT
• 200 OK– DELETE
• 204 No Content– PATCH
• 200 OK
33
応答コード 400番台のユースケース
n⼝座の明細を取得することを考えます。– 該当する⼝座番号がない。
• × 400 Bad Request• ○ 404 Not Found
u 存在しない⼝座の明細を取得する操作は許されません。
– ⼝座は存在するが、その⼝座の明細がない。• × 400 Bad Request• ○ 200 OK
u 存在する⼝座の明細がないことは許される状態です。
34
レスポンス・データのフォーマット
nデータ名のポイントを挙げます。– ⼀般的な英単語を⽤います。– ⼈間が読んで理解できる範囲で短く簡潔なものを⽤います。– 単語を連結する場合、その⽅法を統⼀します。
• JSON ではキャメル・ケース (例: productId, createdAt) を⽤いることが多いです。u JavaScript の命名規約でキャメル・ケースを⽤いることとしているものが多いことから
– 省略形やコードを⽤いる場合は、規格があればそれに従います。– 単数形と複数形に注意します。
• 単数形: 個々のデータを表現します。• 複数形: 配列やリストなど、データの集合を表現します。
35
レスポンス・データの検討 - 性別
n主に sex と gender のいずれかが⽤いられています。– sex: ⽣物的な性別を表します。
• ⼀般に male または female のいずれかの値を取ります。• 0 / 1 で表現することも考えられます。
– gender: 社会的・⽂化的な関係に基づく性を表します。• 多様な値が考えられます。
nどちらを⽤いるかは、API で提供するサービスに基づいて判断するとよいでしょう。– sex: 例) 医療、保険、統計など。– gender: 例) SNS、コマースなど。
36
レスポンス・データの検討 - ⽇付 (1)n⽇付の表現にはいくつかの規格があります。
– RFC 822 / RFC 1123: Thu, 09 Feb 2017 08:07:20 GMT– RFC 3339: 2017-02-09T08:07:20+00:00– ANSI C の asctime() 形式: Thu Feb 9 08:07:20 2017– UNIX タイムスタンプ: 1486595240
37
レスポンス・データの検討 - ⽇付 (2)n現状では RFC 3339 がお勧めです。
– そもそも、こうした「規格の乱⽴」に対処するための規格です。– “Feb” や “Thu” のように特定の⾔語に依存した記法がありません。– 曜⽇を含みません。
• 年⽉⽇に対して曜⽇は⼀意に決定するため、両⽅を記載することは冗⻑です。– スペースを含みません。– タイムゾーンを指定することができます。
• 特段の理由がなければ UTC (+00:00) を⽤いることをお勧めします。
n他の⽅法が適する場合もあります。– アプリケーションで曜⽇を使⽤する場合、曜⽇を含まない RFC 3339 は不便なことが
あります。– 複数のログを取得して後でタイム・スタンプを突き合わせる場合や、世界各地のリ
ソース状態の同期を取るために使⽤する場合は、UTC が適します。⼀⽅、アプリケーションでローカル時刻を使⽤したい場合もあります。
38
エラーの表現
nまず、正確な応答コードを返します。– 400番台: クライアント側 (リクエスト) に起因するエラー– 500番台: サーバー側に起因するエラー
n次に、エラーの詳細情報をできるだけ返します。– 応答ヘッダで返す⽅法があります。– 応答ボディで返す⽅法もあります。
• 現状はボディで返すものが多いようです。• 複数のエラーが同時に発⽣した場合、エラーを配列で返すことも可能です。
nRFC 7807 (2017/05 現在、PROPOSED STANDARD) にて標準化が進められています。– https://tools.ietf.org/html/rfc7807– 上の 3. The Problem Details JSON Object に例が掲載されています。
39
Same-Origin Policy と CORS (1)
nSame-Origin Policy の例 (概説)– Web ブラウザ上で稼働する JavaScript
から API を呼び出すことを考えます。– JavaScript の⽣成元ホストと異なるホ
スト (スキームとポート番号を含む) 上の API の呼び出しは Same-Origin ではなく、原則としてできません。
– 呼び出される API にとって、呼び出し側の JavaScript の⽣成元ホストを信頼できるかは⼀般には不明なためです。
u Same-Origin Policy でのリソースの保護は、この例のケース以外にもあります。
– RFC 6454 で規定されています。• ブラウザによって実装状況は異なります。
1. GET https://www.fintech.com/...
<script>...</script>
2. JavaScript による API 呼び出しを含むコンテンツ
3. GET https://api.bank.com/...
4. SOP 制約により呼び出し不可X
40
Same Origin Policy と CORS (2)
nCORS とは (概説)– Same-Origin Policy の制約を緩和する
⽅法についての規定です。• https://www.w3.org/TR/cors/
– 異なる⽣成元への API 呼び出しではOrigin ヘッダに⽣成元を添付します。
– 呼び出された API では、Origin ヘッダの内容を検査します。信頼できる⽣成元であれば、応答の際に Access-Control-Allow-Origin ヘッダで Origin の内容を返します。
1. GET https://www.fintech.com/...
<script>...</script>
2. JavaScript による API 呼び出しを含むコンテンツ
3. GET https://api.bank.com/...
4. CORS 対応により呼び出し可
Origin: https://www.fintech.com
Access-Control-Allow-Origin: https://www.fintech.com
41
(参考) Same-Origin Policy によらないリソースの保護1. Same-Origin Policy はサーバー上の別の API から呼ばれるなど、
ブラウザ以外での呼び出しは対象外です。– 「ブラウザ上で稼働する JavaScript」からの API 呼び出しが制約の対象です。– ブラウザ以外での呼び出しから API を保護する⽅法として、API Connect では以下
の⽅法があります。• 基本認証, API キー または OAuth
2. 1. で挙げた⽅法を⽤いる場合、呼び出される API で「CORS サポート」を有効にしておきます。
– Same-Origin Policy による保護は不要なためです。– API Connect で CORS サポートを有効にすると、
Origin ヘッダを伴うすべての API 呼び出しが可能になります。
42
API 応答のキャッシュの制御
nAPI サーバー側での応答キャッシュの制御には、主に 2つの⽅法があります。– Expiration Model (期限切れモデル)
• サーバーは応答の際に、内容の有効期限を通知します。– Validation Model (検証モデル)
• サーバーは応答の際に、最終更新⽇時やエンティティ・タグ (Etag) を通知します。
n「キャッシュを禁じる」指定も可能です。– Cache-Control: no-cache, no-store, must-revalidate– Expires: 0
43
Expiration Model (期限切れモデル)
nサーバーがクライアントなどに対して、応答内容の有効期限を通知します。– クライアントは API の前回呼び出しの際に受け取った有効期限と現在の時刻を⽐較し、
期限が切れていれば新しい情報を⼊⼿するために改めて API を呼び出します。n有効期限の通知⽅法
– 絶対時刻: Expires ヘッダを使⽤します。• Expires: Mon, 13 Feb 2017 06:44:00 GMT• RFC 2616 により、時刻の表現には RFC 1123 形式を⽤いなくてはなりません。
– 応答した時刻からの経過時間: Cache-Control ヘッダを使⽤します。• Cache-Control: max-age=3600• 応答内容の有効期限が 3600(秒) 後であることを⽰します。
44
Validation Model (検証モデル)
nサーバーはクライアントに対して、応答内容の「状態」を通知します。– クライアントは API の前回呼び出し時に受け取った状態を、今回の呼び出し時にサー
バーに送信します。– サーバーは、クライアントから提⽰された状態と現在の状態を⽐較します。
• 状態が異なれば、応答コード 200 OK とともに現在の状態の応答を返します。• 状態が同じであれば、応答コード 304 Not Modified により、状態が変わっていないことをクラ
イアントなどに知らせます。
n「状態」の通知⽅法– 最終更新時刻: Last-Modified ヘッダを使⽤します。
• Last-Modified: Thu, 16 Feb 2017 08:12:18 GMT– ETag (エンティティ・タグ): ETag ヘッダを使⽤します。
• ETag: “e34ac5678ff96453bd9c76389ed8412b”• ETag の⽣成⽅法は API サーバーの実装に任されています。
45
⼤量呼び出しへの対策 - レート・リミット (1)
nAPI 呼び出し数や頻度を無制限に許容できないことがあります。– 契約に基づいて、API 呼び出し数や頻度を制限したい場合があります。
• API ユーザーが契約を超える API 呼び出しをしたい場合は、別途追加料⾦を徴収するなど。u この節ではこのケースを扱います。
– DoS 攻撃に対する防御など、リソースの保護を⽬的とする場合は、ロードバランサやプロキシなど、ネットワークの観点から対策を検討して下さい。
u この節では扱いません。
46
⼤量呼び出しへの対策 - レート・リミット (2)
n適切な尺度を⽤いて、API 呼び出しに制限を加えることが必要です。– 制限の単位
• ユーザー• アプリケーション• IP アドレス• ...
– 制限する時間窓– 制限の単位 / 時間窓 あたりの呼び出し数
47
レート・リミットをユーザーに知らせる⽅法 (1)
n開発者ポータル– IBM API Connect では、アプリケー
ション開発者が開発者ポータルで、API 製品のレート・リミットを知ることができます。
nAPI Designer / API Manager– IBM API Connect では、API 提供者が
API Designer または API Manager を使⽤して、API 製品のレート・リミットを設定することができます。
48
レート・リミットをユーザーに知らせる⽅法 (2)
n応答ヘッダで API のレート・リミットを知らせることができます。– 標準化されたものは現在のところありませんが、X-RateLimit- で始まるヘッダが事実
上の標準となっています。• http://stackoverflow.com/questions/16022624/examples-of-http-api-rate-limiting-http-
response-headers– IBM API Connect では、API 製品にレート・リミットを設定 (前ページ) すると、設
定に応じて以下のヘッダのうち必要なものが API 応答に⾃動的に付加されます。• X-RateLimit-Limit• X-RateLimit-Remaining• X-RateLimit-Reset
– (参考) IBM API Connect では、キャッシュの検証 (p. 44) では OPTIONS メソッドを使⽤しており、レート・リミットのリクエスト数には計上されません。
49
レート・リミットを超えてしまった場合の応答 (1)
n応答コード 429 Too Many Requests を返します。– RFC 6585– 状況の詳細説明を返すべきとされています(SHOULD)。– リクエストが受け付けられるようになるまでの待ち時間を Retry-After ヘッダで返し
てもよいとされています(MAY)。– RFC 6585 には以下の例が記載されています。
HTTP/1.1429 Too Many RequestsContent-Type: text/htmlRetry-After: 3600
<html><head>
<title>Too Many Requests</title></head>
<body><h1>Too Many Requests</h1><p>I only allow 50 requests per hour to this Web site per logged in user. Try again
soon.</p></body>
</html>
50
レート・リミットを超えてしまった場合の応答 (2)
n IBM API Connect ではレート・リミットを超えた場合、ハード制限の強制が有効になっていると以下の応答が⾃動的に返されます。– 応答コード: 429– 応答ボディ:
n IBM API Connect では、レート・リミットはアプリケーション (クライアント ID) ごとに管理されています。
• あるアプリケーションからのある製品の API 呼び出しがレート・リミットを超えてしまっても、別のアプリケーションからの呼び出しは (レート・リミットを超えていなければ) 可能です。
{"httpCode": "429","httpMessage": "Too Many Requests","moreInformation": "Rate Limit exceeded”
}
52
API へのリクエスト
nリクエスト・ボディがある場合は、Content-Type ヘッダを設定します。– Content-Type: application/json など
nAccept ヘッダで、応答で受信可能なメディア・タイプを指定します。– Accept: application/json など
nAccept を複数指定することができます。– Accept: application/json, application/xml
• 「JSON と XML のいずれも受け取ることができる」ことを表します。
nAccept を優先度付きで複数指定することができます。– Accept: application/json, application/xml; q=0.9
• 「JSON を優先して受け取りたいが、XML でも可」 を表します。• q は Quality Value です。⼤きい値を持つメディア・タイプの優先度が⾼いことを⽰します。
u 省略時の値は 1 と解釈されます。
53
Server Driven Content Negotiation
n前ページの優先度付き Accept を解釈して、API 側でレスポンスのメディア・タイプを決定することを指します。– Sever Driven Content Negotiation をサポートする場合、レスポンス・ヘッダに以
下を指定する必要があります。• Vary: Accept
u Accept ヘッダの内容によって、レスポンスの内容が変わりうることを表します。
• 経路上にキャッシュが存在する場合、Accept ヘッダの内容によってどのレスポンスをキャッシュするか、キャッシュが判断する際に⽤いられます。
55
URI での⽇本語の使⽤
nAPI 設計として⼀般には避けるべきです。– エンコーディングによってコードが変わります。
• URI だけ⾒ても内容の理解が⼀意になりません。
nクエリー・パラメーターでは、避けられない場合があるかもしれません。– http://api.example.com/users?q=⽇本語– 予期しない動作を防ぐため、各プラットフォームで⽤意された⽅法を⽤いてエンコード
します。• Java: java.net.URLEncoder• JavaScript: encodeURI や encodeURIComponent
56
JSON での⽇本語
nUnicode を使⽤するように決められています。– RFC 4627 3. Encoding
• JSON text SHALL be encoded in Unicode. The default encoding is UTF-8.
58
API 設計・実装の変更とユーザーへの通知の必要性
n公開済みの API を変更する場合、変更を API ユーザーに知らせるべき場合と、知らせなくてよい場合があります。– 知らせるべき場合: API 設計 (= インターフェース または 仕様) の変更
• API ユーザーから⾒た際の API の使⽤⽅法が変わるため、変更をユーザーに知らせなくてはなりません。
– 知らせなくてよい場合: API 実装の変更• API ユーザーから⾒た際の API の使⽤⽅法は変わらないため、変更をユーザーに知らせる必要
はありません。• バグの修正などが当てはまります。
59
API 設計変更の通知⽅法
nAPI のバージョニングによる⽅法が広く使われています。– セマンティック・バージョニング (次ページ)
nAPI のバージョンをどう指定するか– URI に埋め込む
• http://api.example.com/v1/users– クエリ・パラメータに設定する
• http://api.example.com/users?v=2– メディア・タイプを使⽤する
• リクエスト: Accept ヘッダu Accept: application/vnd.example.v2+json
• 応答: Content-Type ヘッダu Content-Type: application/vnd.example.v2+jsonu Vary: Accept
60
バージョン番号をどう付けるか
nセマンティック・バージョニング、もしくはこれに似た⽅法が⽤いられています。– http://semver.org/lang/ja/ より引⽤
• バージョンナンバーは、メジャー.マイナー.パッチとし、バージョンを上げるには、1. APIの変更に互換性のない場合はメジャーバージョンを、2. 後⽅互換性があり機能を追加した場合はマイナーバージョンを、3. 後⽅互換性を伴うバグ修正をした場合はパッチバージョンを上げます。
nAPI のバージョンを URI などに埋め込む (前ページ) 場合はメジャーのみとし、マイナーとパッチは埋め込みません。– 2. と 3. は p. 58 の「知らせなくてよい場合」であるためです。
61
API 公開の終了
n公開済み API (のバージョン) を予告なしに停⽌することは、API ユーザー (アプリケーション) や、アプリケーションのユーザーに対する影響が⼤きくなることがあります。
n以下に留意して、影響の最⼩化を図ります。– 適切な⽅法で継続して告知します。
• 数ヶ⽉~1年、もしくはそれ以上かけて実施します。– API 停⽌テストを実施します。
• ⼀度でなく数回実施します。– API 仕様に API 公開の終了時の動作を規定しておきます。
• 410 Gone とともに公開終了のメッセージを返す、など。– API 利⽤規約に API 公開の終了に関する項⽬を規定しておきます。
63
【参考】Swagger 2.0 http://swagger.io
n RESTful API の仕様や⽂書を記述するための標準仕様です。– RESTful API で使⽤可能なリソースのリストと,それらのリソースに対する操作を規定します。– パラメーターの名前とタイプ,必須 or オプション,取れる値などを規定します。– JSON or YAML でデータを表現します。
n RESTful API 開発に役⽴つツール群があります。– Swagger Editor
• Swagger ⽂書の作成を⽀援するツールです。– Swagger UI
• Swaggerで定義されたAPIをビジュアルにテストできます。– Swagger CodeGen
• Swagger⽂書から様々な⾔語での呼び出しコードの⽣成が可能です。
Swagger UI 画面
64
【参考】REST API の開発
{ "swagger" : "2.0","info" : {"description" : "Discover REST APIs available within Liberty","version" : "1.0.0","title" : "Liberty REST APIs"
},"host" : "$(catalog.host)","paths" : {"/xyzCop/rest/" : {"get" : {"operationId" : "news","parameters" : [ ],"responses" : {"200" : {"description" : "successful operation","schema" : { "type" : "string" },"headers" : { }
}・・・(省略)
Swagger文書サンプル
n ボトム・アップでの開発が可能です。– コード開発者が API のアーキテクチャもデザインするケースです。– JAX-RS アプリのコード作成し、コードのアノテーションから Swagger ⽂書を⽣成して公開します。
n トップ・ダウンで開発することも可能です。– コード実装前に API を設計して開発します。– Swagger ツールなどを利⽤して Swagger ⽂書を開発します。
• 開発された Swagger ⽂書をそのまま公開し、RESTful API コードはアノテーションなしで実装します。
65
【参考】Swagger ⽂書からの RESTful API コード⽣成n JAX-RS クライアント / サービスのソースコード⾃動⽣成が可能です。
– Eclipse の WDT (Web application Developer Toolkit) プラグインの機能により、既存のSwagger ⽂書から RESTful API の JAX-RS クライアント / サービスのコードを⽣成することが可能です。
– 以下の IBM 製品でも Swagger ⽂書からのソースコード⾃動⽣成をサポートしています。• API Connect (API Designer), Integration Bus (Integration Toolkit), App Connect (App Connect Studio)
67
RESTful API 実装 〜 JavaScript 編 ⽬次n フレームワーク概説n 開発環境とツールn プロジェクトのセットアップn パスの解釈n HTTP メソッドとリソースに対する操作n レスポンス・ボディの作成n エラー・ハンドリングn サーバーからのレスポンスの送信n リクエストの実⾏n 実⾏・テスト環境: 開発⽤ PC の場合n 実⾏・テスト環境: Bluemix の場合n 参考資料
68
フレームワーク概説
nこのガイドで使⽤する環境– Node.js
• サーバーサイド JavaScript 実⾏環境
nこのガイドで使⽤するフレームワーク– Express4
• Node.js で動作する MVC フレームワークで、Web アプリケーションの迅速な開発、また REST API 開発の効率化を実現します。
• Express4 からは express-generatorを使い、アプリケーションの雛形をコマンドで作成します。• Express3 とは使い⽅や起動⽅法が異なるため、情報を探す際にはどちらのバージョンをベースに
しているか注意が必要です。
69
開発環境とツールnこのガイドで使⽤する開発環境とツール⼀覧
– このガイドでは Bluemix 上にデプロイした Cloudant のドキュメントに対して、CRUD 操作を⾏う RESTful API を開発します。• 下記のサンプル DB ⼀覧から今回は animaldb をレプリケーションし、ドキュメントを CRUD 操作する API を作成します。
u https://developer.ibm.com/clouddataservices/docs/cloudant/get-started/• サンプル DB のレプリケーション⽅法は以下をご参照ください
u https://developer.ibm.com/clouddataservices/docs/cloudant/database-replication/create-a-replication-job/
エディタ
Atom v1.15.0https://atom.io/
実⾏環境
Node.js v6.9.4https://nodejs.org/ja/
フレームワーク
Express v4.14.1http://expressjs.com/en/starter/installing.html
ライブラリ
Cloudant library for Node.jshttps://www.npmjs.com/package/cloudant
データベース
Cloudant NoSQL DBhttps://console.ng.bluemix.net/catalog/services/cloudant-nosql-db
コマンドツール
Express-generator http://expressjs.com/en/starter/generator.html
テストツール
Postmanhttps://www.getpostman.com/
※ 各環境、ツールなどの導⼊⼿順はリンク先にに従うものとして、本ガイドでは省略します。
70
プロジェクトのセットアップ■Node.js / Express4 / Express-generator 及び Cloudant ライブラリ、サンプル DBはセットアップ済みであることを前提とします。■express コマンドでプロジェクトのセットアップ1. $ express restapi2. $ cd restapi3. $ npm install (package.jsonに記載されている依存ライブラリの追加インストール)4. $ npm start (起動コマンド)これにてアプリの雛形作成が完了し、http://localhost:3000 にアクセスすると、サンプル・アプリが起動済みとなります。ここに今回開発するコードを加えていきます。
71
パスの解釈
nAPI エンドポイントの設計は、このガイド前半の「RESTful API 設計」の節に従います。特に以下に留意します。– パスは英語の名詞を使うこと(複数形と単数形の使い分けに注意します)– 短く⼈間にわかりやすいこと– ⼩⽂字で統⼀すること
nこのガイドで開発するサンプル API のパスは以下のように設定します。– 今回はクエリ・パラメータは使⽤せず、パスのみ設定します。Øコレクション・リソースのエンドポイント
• https://<hostname>[:port]/animals/Øインスタンス・リソースのエンドポイント
• https://<hostname>[:port]/animals/<_id>• https://<hostname>[:port]/animals/<_id>/<_rev>
72
HTTP メソッドとリソースに対する操作
n このガイドでは、例として以下のように dog という Cloudant ドキュメントに対して Create, Read, Update, Delete を⾏います。
1. POST https://<hostname>:3000/animals/dog– ID が dog のドキュメントを作成– 作成するドキュメントはリクエスト・ボディに記述
2. GET https://< hostname >:3000/animals/dog– ID が dog のドキュメントを取得
3. PUT https://< hostname >:3000/animals/dog– ID が dog であるドキュメントのすべての属性を更新– 更新する属性とその値はリクエスト・ボディに記述
4. DELETE https://< hostname >:3000/animals/dog/_rev– ID が dog で、かつ指定した rev 番号のドキュメントを削除
73
レスポンス・ボディの作成
n GETn 200 OK
n GET した JSON ドキュメントを返す例)
■POSTn201 Created
n 作成したリソース id と rev と URI を返す例)
nGET, CREATE, UPDATE, DELETE それぞれの成功時のレスポンスを作成します。– このガイドでは Cloudant からレスポンスを受け取り、追加項⽬を加えます。
{ "document":
{ "_id": "dog", "_rev": "66-6d3151ceff0f0204690f14d4123771dc", "min_weight": 7, "max_weight": 50, "min_length": 5, "max_length": 7, "latin_name": "Canis", "wiki_page": "http://en.wikipedia.org/wiki/Dog", "class": "mammal", "diet:": "carnivore” }
}
{”created":
{ "ok":true,"id":"dog","rev":"1-89559cb0c86cbd319867347a65d6d182”},
"url":”./animals/dog”}
74
レスポンス・ボディの作成
nDELETEn204 No Content
n 削除したリソースの id, rev を返す例)
nGET, CREATE, UPDATE, DELETE それぞれの成功時のレスポンスを作成します。– このガイドでは Cloudant からレスポンスを受け取り、追加項⽬を加えます。
nPUTn200 OK
n id と新しく割り振られた rev を返す例)
{" updated”:
{"ok":true,"id":”dog","rev":"18-f1e9fe44fc5b399562c729e64fe29ae6”}
}
{ ”deleted":
{ "ok": true,"id": "dog","rev": "41-34e6354a1825919bf80225f0ae1432a8" }
}
75
エラー・ハンドリング
n GETn 404 Not Found
n 正しい id を指定する旨を記述n 500 Internal Server Error
n サーバー側に問題があるので後で再試⾏する旨を記述例)
nGET, CREATE, UPDATE, DELETE それぞれの失敗時のレスポンスを作成します。– Cloudant が返す応答コードを受け取り、エラー・ハンドリングを実装します。
n POSTn 409 Conflict
n 同⼀リソースが存在している旨を記述n 500 Internal Server Error
n サーバー側に問題があるので後で再試⾏するように記述例)
{“error”: {“code”: 404,“message”: “Resource not found, specify the correct id”,“info”: “https://docs.cloudant.com/http.html#http-status-codes”}
}
{“error”: {“code”: 409,“message”: “Resource already exists”,“info”: “https://docs.cloudant.com/http.html#http-status-codes”}
}
76
エラー・ハンドリング
nDELETEn 404 Not Found
n 正しい idとrev を指定するように記述n409 Conflict
n 正しい id と revを指定するように記述n500 Internal Server Error
n サーバー側に問題があるので後で再試⾏するように記述
n 例)
nGET, CREATE, UPDATE, DELETEそれぞれに失敗時のレスポンスを作成します。– Cloudant が返す応答コードを受け取り、エラー・ハンドリングを実装します。
n PUTn 404 Not Found
n 正しい idとrev を指定するように記述n409 Conflict
n 正しい id と revを指定するように記述n500 Internal Server Error
n サーバー側に問題があるので後で再試⾏するように記述
n 例)
{“error”: {“code”: 409,“message”: “Update failed, specify the correct id and rev”,“info”: “https://docs.cloudant.com/http.html#http-status-codes”}
}
{“error”: {“code”: 500,“message”: “Internal Server Error, retry later”,“info”: “https://docs.cloudant.com/http.html#http-status-codes”}
}
77
サーバーからのレスポンスの送信n レスポンス・ヘッダには以下を設定します。
– Cloudant が返すヘッダを API 実装でそのまま返します。• Connection• Content-Length• Content Type: application/json ; charset=utf-8• Date• ETag• X-Powered-By
78
リクエストの実⾏n API エンドポイントに対する HTTP リクエストで、以下を指定します。
– メディア・タイプ:• Content-Type: application/json
– リクエスト・ボディ:• POST
• GET と DELETE にはリクエスト・ボディは指定しません。– URI:
• p. 22 (HTTP メソッドとリソースに対する操作例 (1)) を参照下さい。
{ "_id": ”dog", "min_weight": 5, "max_weight": 30, "min_length": 1, "max_length": 1.5, ”latin_name": " Canis ", "wiki_page": "http://en.wikipedia.org/wiki/Dog", "class": "mammal", "diet:": "carnivore”
}
{"_id": ”dog", "_rev": "16-06c5fd01b35bfbe455465dcdc12670ba", "min_weight": 5, "max_weight": 30, "min_length": 2, "max_length": 4, ”latin_name": " Canis ", "wiki_page": "http://en.wikipedia.org/wiki/Dog", "class": "mammal", "diet:": "carnivore”
}
• PUT
79
実⾏・テスト環境: 開発⽤ PC の場合
n以下のコマンドで Express サーバーを起動します。– $ cd restapi– $ npm start
2. localhost にデプロイされた API エンドポイントへ GET した結果
1. 起動されたことを確認
3. Express4 のコンソールに出⼒される結果
84
実⾏・テスト環境: Bluemix の場合
n作成した API プロジェクトを Bluemix にデプロイ– $ cd restapi– $ cf push <Bluemix上で使うアプリ名> (CLI で Bluemixにログインしていることが前提)
1. Node.js ビルドバックを使ってデプロイされたことを確認
2. Bluemix 上にデプロイされた API エンドポイントへの GET の結果
※ cf push前に、使⽤しているnode-moduleとpackage.jsonの記述が⼀致しているか再確認することをおすすめします。不⼀致があると以下のようなエラーが出て始動に失敗します。
85
[参考] Swagger ⽂書と API Designern 当ガイドではボトムアップ⼿法を採⽤していますが、トップダウン⼿法で API を定義した Swagger ⽂書からコード⽣成することも可能です。n Swagger Editorはエラーとその内容も表⽰してくれるため、正しい API 定義を作成するのに有⽤です。n Swagger で記述した YAML ファイルを、IBM API Connect が提供する開発ツールである API Designer に取り込んだり、あるいは最初から
API Designer で GUI を使⽤して API 定義を作成することもできます。
yamlファイルのインポート
86
[参考] サンプル・コード
n今回開発した成果物は以下です。– app.js
• メイン・スクリプト– .env
• Cloudantクレデンシャル記述ファイルu 各⾃の環境⽤のクレデンシャルを記述してください
– routes/cloudantClient.js• メイン・スクリプトから読み込む CRUD 操作の実装本体スクリプト
nexpress コマンドでアプリケーションの雛形を作成後、上のファイルを置き換えて下さい。
87
[参考] サンプル・コード - app.js (1)
var express = require('express');var path = require('path');var favicon = require('serve-favicon');var logger = require('morgan');var cookieParser = require('cookie-parser');var bodyParser = require('body-parser');var cloudantClient = require('./routes/cloudantClient');var app = express();
app.use(logger('dev'));app.use(bodyParser.json());app.use(bodyParser.urlencoded({ extended: true }));app.use(cookieParser());app.use(express.static(path.join(__dirname, 'public')));app.use('/animals', cloudantClient);
88
[参考] サンプル・コード - app.js (2)
// catch 404 and forward to error handlerapp.use(function(req, res, next) {var err = new Error('Not Found');err.status = 404;next(err);});
// error handlerapp.use(function(err, req, res, next) { // set locals, only providing error in developmentres.locals.message = err.message;res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page res.status(err.status || 500);res.render('error');});
module.exports = app;
89
cloudant_username=<Cloudant ユーザー名をここに記載します>cloudant_password=<Cloudant パスワードをここに記載します>
[参考] サンプル・コード - .env
90
[参考] サンプル・コード - routes/cloudantClient.js (1)require('dotenv').load();
var express = require('express');var router = express.Router();
var Cloudant = require('cloudant');var me = process.env.cloudant_username; // Set this in .env filevar password = process.env.cloudant_password; // Set this in .env filevar cloudant = Cloudant({account:me, password:password}); // Set my accountvar db = cloudant.db.use("animaldb");
91
[参考] サンプル・コード - routes/cloudantClient.js (2)router.get('/:_id', function(req, res, next) {db.get(req.params._id, function(err, data) {if (!err) {res.status(200).json({"document":data});console.log(data);
} else if(err.statusCode == 404) {console.log("Error:", err.statusCode);res.status(404).json({"error":{"code":404,"message":"Resource not found. Specify the correct
id.","info":"https://docs.cloudant.com/http.html#http-status-codes"}});} else if(err.statusCode == 500) {console.log("Error:", err.statusCode);res.status(500).json({"error":{"code":500,"message":"Internal Server Error. Please retry
later.","info":"https://docs.cloudant.com/http.html#http-status-codes"}});} else {console.log("Error:", err);res.status(err.statusCode).json({"error":{"code":err.statusCode,"message":"Other
error.","info":"https://docs.cloudant.com/http.html#http-status-codes"}});}
});});
92
[参考] サンプル・コード - routes/cloudantClient.js (3)router.post('/', function(req, res, next) {db.insert(req.body, req.body._id, function(err, data) {if(!err) {var id = req.body._id;var url = './animals/'+ id;res.status(201).json({"created":data,"url":url});console.log(data);
} else if(err.statusCode == 409) {console.log("Error:", err.statusCode);res.status(409).json({"error":{"code":409, "message":"Resource already
exists.","info":"https://docs.cloudant.com/http.html#http-status-codes"}});} else if(err.statusCode == 500) {console.log("Error:", err.statusCode);res.status(500).json({"error":{"code":500,"message":"Internal Server Error. Please retry
later.","info":"https://docs.cloudant.com/http.html#http-status-codes"}});} else {console.log("Error:", err);res.status(err.statusCode).json({"error":{"code":err.statusCode,"message":"Other
error.","info":"https://docs.cloudant.com/http.html#http-status-codes"}});}
});});
93
[参考] サンプル・コード - routes/cloudantClient.js (4)router.put('/:_id', function(req, res, next) {db.insert(req.body, function(err, data) {if (!err) {res.status(200).json({"updated":data});console.log(data);
} else if (err.statusCode == 409) {console.log("Error:", err.statusCode);res.status(409).json({"error":{"code":409,"message":"Update failed. Specify the correct id and
rev.","info":"https://docs.cloudant.com/http.html#http-status-codes"}});} else if(err.statusCode == 500) {console.log("Error:", err.statusCode);res.status(500).json({"error":{"code":500,"message":"Internal Server Error. Please retry
later.","info":"https://docs.cloudant.com/http.html#http-status-codes"}});} else {console.log("Error:", err);res.status(err.statusCode).json({"error":{"code":err.statusCode,"message":"Other
error.","info":"https://docs.cloudant.com/http.html#http-status-codes"}});}
});});
94
[参考] サンプル・コード - routes/cloudantClient.js (5)router.delete('/:_id/:_rev', function(req, res, next) {db.destroy(req.params._id,req.params._rev, function(err, data) {if (!err) {res.json({"deleted":data});res.status(204);console.log(data);
} else if(err.statusCode == 404) {console.log("Error:", err.statusCode);res.status(404).json({"error":{"code":404,"message":"Resource not found. Specify the correct id
and rev.","info":"https://docs.cloudant.com/http.html#http-status-codes"}});} else if(err.statusCode == 409) {console.log("Error:", err.statusCode);res.status(409).json({"error":{"code":409,"message":"Delete failed. Specify the correct id and
rev.","info":"https://docs.cloudant.com/http.html#http-status-codes"}});} else if(err.statusCode == 500) {console.log("Error:", err.statusCode);res.status(500).json({"error":{"code":500,"message":"Internal Server Error. Please retry
later.","info":"https://docs.cloudant.com/http.html#http-status-codes"}});} else {console.log("Error:", err);res.status(err.statusCode).json({"error":{"code":err.statusCode,"message":"Other
error.","info":"https://docs.cloudant.com/http.html#http-status-codes"}});}
});});
module.exports = router;
96
⽬次n フレームワーク概説n 開発環境とツールn プロジェクトのセットアップn パスの解釈n HTTP メソッドとリソースに対する操作n レスポンス・ボディの作成n エラー・ハンドリングn サーバーからのレスポンスの送信n リクエストの実⾏n 実⾏・テスト環境:ローカルPCの場合n 実⾏・テスト環境:Bluemixの場合n WAS Liberty API Discovery フィーチャーn SwaggerからのREST API コード⽣成n 参考資料
97
フレームワーク概説
nこのガイドで使⽤する環境– WebSphere Application Server Liberty Profile (WAS Liberty)
• 従来の WAS (WAS traditional) を軽量化した Java EE の実⾏環境です。• JAX-RS、JMS などサポートするフィーチャーを柔軟に組み合わせることが可能です。• Eclipse のプラグインとして無償提供されている WDT のテスト⽤サーバーとして使⽤することが
可能です。
nこのガイドで使⽤するフレームワーク– JAX-RS
• RESTful API の開発に特化した Java のフレームワークです。• Java のソースコードにアノテーションを加えることで、RESTful Web サービスを容易に公開す
ることが可能です。
98
開発環境とツールnこのガイドで使⽤する開発環境とツール⼀覧
– このガイドでは Bluemix 上にデプロイした Cloudant のドキュメントに対して、CRUD 操作を⾏う RESTful API を開発します。• 下記のサンプル DB ⼀覧から今回は animaldb をレプリケーションし、ドキュメントを CRUD 操作する API を作成します
u https://developer.ibm.com/clouddataservices/docs/cloudant/get-started/• サンプル DB のレプリケーション⽅法は以下をご参照ください
u https://developer.ibm.com/clouddataservices/docs/cloudant/database-replication/create-a-replication-job/
実⾏環境• WebSphere Application Server Liberty Profile• IBM Eclipse Tools for Bluemixhttps://www.ibm.com/developerworks/jp/cloud/library/j_cl-bluemix-java-app/
フレームワークJAX-RS 2.0https://jax-rs-spec.java.net/
ライブラリ
A Java client for Cloudanthttps://github.com/cloudant/java-cloudant
データベース
Cloudant NoSQL DBhttps://console.ng.bluemix.net/catalog/services/cloudant-nosql-db
開発ツール• Eclipse Neon• IBM WebSphere Application Server Liberty
Developer Tool for Neon(WDT)
テストツールPostmanhttps://www.getpostman.com/
※ 各環境、ツールなどの導⼊⼿順はリンク先にに従うものとして、本ガイドでは省略します。
99
プロジェクトのセットアップn Java、Eclipse(plug-in)、Cloudant ライブラリ、サンプル DB はセットアップ済みである
ことを前提とします。n Eclipseでのプロジェクトのセットアップ
– Webプロジェクトを作成し、以下のように設定• Webテンプレートタイプ → シンプル• プログラミングモデル → Java EE• フィーチャーの変更 → 「JAX-RS (REST Webサービス)」を追加• JAX-RS 実装ライブラリ → 「IBM WebSphere Application Server <Version> JAX-RSライブラリ」を追加
– JAX-RS が使⽤可能なプロジェクトが作成されます。– ここに今回開発するコードを加えます。
n 参考URL– https://www.ibm.com/support/knowledgecenter/ja/SSHR6W/com.ibm.websphere.wdt.doc/topics/core/tja
xrswebservcreate.htm
100
パスの解釈
nAPI エンドポイントの設計は、このガイド前半の「RESTful API 設計」の節に従います。特に以下に留意します。– パスは英語の名詞を使うこと(複数形と単数形の使い分けに注意します)– 短く⼈間にわかりやすいこと– ⼩⽂字で統⼀すること
nこのガイドで開発するサンプル API のパスは以下のように設定します。– 今回はクエリ・パラメータは使⽤せず、パスのみ設定します。Øコレクション・リソースのエンドポイント
• https://<hostname>[:port]/animals/Øインスタンス・リソースのエンドポイント
• http://<hostname>[:port]/animals/<_id>
101
HTTP メソッドとリソースに対する操作
n このガイドでは、例として以下のように dog という Cloudant ドキュメントに対して Create, Read, Update, Delete を⾏います。
1. POST http://<hostname>:9080/animals– ID が dog であるドキュメントを作成– 作成するドキュメントはリクエスト・ボディに記述
2. GET http://< hostname >:9080/animals/dog– ID が dog であるドキュメントを取得
3. PUT http://< hostname >:9080/animals/dog– ID が dog であるドキュメントのすべての属性を更新– 更新する属性とその値はリクエスト・ボディに記述
4. DELETE http://< hostname >:9080/animals/dog– ID が dog であるドキュメントを削除
102
レスポンス・ボディの作成
n GETn 200 OK
n GETしたJSONのドキュメントを返す
例)
■POSTn 201 Created
n Cloudantに新たに投⼊されたJSONドキュメントを返す例)
nGET, CREATE, UPDATE, DELETE それぞれの成功時のレスポンスを作成します。– このガイドでは Cloudant からレスポンスを受け取り、追加項⽬を加えます。
103
レスポンス・ボディの作成
nDELETEn 204 No Content
n レスポンス・ボディはない
nGET, CREATE, UPDATE, DELETE それぞれに成功時のレスポンスを作成します。– このガイドでは Cloudant からレスポンスを受け取り、追加項⽬を加えます。
n PUTn 200 OK
n 更新されたJSONドキュメントを返す例)
104
エラー・ハンドリング
n GETn 404 Not Found
n リソースが存在しない旨を記述するn 500 Internal Server Error
n サーバー側に問題があるので後で再試⾏する旨を記述する
nGET, CREATE, UPDATE, DELETE それぞれの失敗時のレスポンスを作成します。– Cloudant が返す応答コードを受け取り、エラー・ハンドリングを実装します。
n POSTn 409 Conflict
n 競合が発⽣している旨を表⽰するn 500 Internal Server Error
n サーバー側に問題があるので後で再試⾏する旨を記述する
例)
例)
105
エラー・ハンドリング
n DELETEn 404 Not Found
n リソースが存在しない旨を記述するn 500 Internal Server Error
n サーバー側に問題があるので後で再試⾏するように記述する
n PUTn 404 Not Found
n リソースが存在しない旨を記述するn 500 Internal Server Error
n サーバー側に問題があるので後で再試⾏するように記述する
nGET, CREATE, UPDATE, DELETE それぞれに失敗時のレスポンスを作成します。– Cloudant が返す応答コードを受け取り、エラー・ハンドリングを実装します。
106
サーバーからのレスポンスの送信
nレスポンス・ヘッダには以下を設定します。– Cloudant が返すヘッダをそのまま返します。
• Connection: keep-alive• Content-Length• Content Type: application/json ; charset=utf-8• Date• ETag• X-Powered-By
107
実⾏・テスト環境: 開発⽤ PC の場合
nEclipse テスト環境の WAS Liberty で API エンドポイントを起動します。
1. 作成したAPIをWAS Libertyに追加 2. WAS Libertyを起動
3. localhost にデプロイされた API エンドポイントへGET した結果
112
実⾏・テスト環境: Bluemix の場合
n 作成した API プロジェクトを Eclipse から Bluemix にデプロイ– IBM Eclipse Tools for Bluemix を使って作成したアプリケーションを簡単に公開できる– アプリケーション単体もしくは WAS Liberty サーバーごとデプロイすることが可能– 下の参照先 URL の⼿順に従ってデプロイ:
https://www.ibm.com/developerworks/jp/cloud/library/j_cl-bluemix-java-app/
Bluemix 上にデプロイされた API エンドポイントへの GET 結果
113
n RESTful API の情報を公開する WAS Liberty の機能です。n Swagger ⽂書形式の API ドキュメンテーションを公開します。
– REST API を公開したい Liberty サーバーで apiDiscovery-1.0 を有効化します。– アプリケーションにコードの追加なしに Swagger での情報提供が可能です。– Web モジュール毎に RESTful API のインターフェース情報を集約して提供します。– API の探索・テストを⾏えるグラフィカルなページを提供します。
n 参考URL:https://www.ibm.com/support/knowledgecenter/ja/SS7K4U_liberty/com.ibm.websphere.wlp.zseries.doc/ae/twlp_api_discovery.html
[参考] WAS Liberty API Discovery フィーチャー
https://<hostname>:9443/ibm/api/docs https://<hostname>:9443/ibm/api/explorer
Swagger 文書が自動生成される Liberty サーバー上の API の探索・テストも可能
WAS Libertyの server.xml に apiDiscoveryフィーチャーを加えると・・・
114
[参考] サンプル・コード
n今回開発した成果物は以下です。– CloudantRestClientResource.java
• JAX-RS リソース・クラス– CloudantRestClient.java
• JAX-RS アプリケーション・クラス– ErrorInfo.java
• エラー情報の格納⽤クラス– Animal.java
• animaldb 上のドキュメントを表現するクラス
n「プロジェクトのセットアップ」のページに従ってプロジェクトを作成後、上のファイルを置き換えて下さい。
115
[参考] サンプル・コード - CloudantRestClientResource.java (1)
package animal.web.api;
import java.io.IOException;import java.net.*;import javax.ws.rs.*;
import com.cloudant.client.api.*;import com.cloudant.client.org.lightcouch.*;
import animal.web.api.Animal;
import com.ibm.websphere.security.NotImplementedException;
116
[参考] サンプル・コード - CloudantRestClientResource.java (2)
@Path("/animals")public class CloudantRestClientResource {
Database db;
public CloudantRestClientResource() throws IOException {
CloudantClient client = ClientBuilder.url(new URL("https://xxxxx.cloudant.com")).username(”<Cloudant ユーザー名>").password(”<Cloudant パスワード>").disableSSLAuthentication().build();
db = client.database("animaldb", false);System.out.println("Server Version" + client.serverVersion());
}
private ErrorInfo createErrorInfo(Exception e, Status status) {ErrorInfo errorInfo = new ErrorInfo();errorInfo.setStatusCode(status.getStatusCode());errorInfo.setMessage(e.getMessage());return errorInfo;
}
117
[参考] サンプル・コード - CloudantRestClientResource.java (3)@GET@Path("/{_id}")@Produces("application/json")
public Response responseGet(@PathParam("_id") String _id) {
try {Animal animalObject = db.find(Animal.class, _id);ResponseBuilder rb = Response.ok(animalObject);return rb.build();
} catch (BadRequestException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.BAD_REQUEST);return Response.status(Status.BAD_REQUEST).entity(errorInfo).build();
} catch (NoDocumentException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.NOT_FOUND);return Response.status(Status.NOT_FOUND).entity(errorInfo).build();
} catch (NotAcceptableException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.METHOD_NOT_ALLOWED);return Response.status(Status.METHOD_NOT_ALLOWED).entity(errorInfo).build();
} catch (InternalServerErrorException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.INTERNAL_SERVER_ERROR);return Response.status(Status.INTERNAL_SERVER_ERROR).entity(errorInfo).build();
} catch (ServiceUnavailableException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.SERVICE_UNAVAILABLE);return Response.status(Status.SERVICE_UNAVAILABLE).entity(errorInfo).build();
}}
118
[参考] サンプル・コード - CloudantRestClientResource.java (4)
@POST@Consumes("application/json")@Produces("application/json")public Response responsePost(Animal animalCreate) throws URISyntaxException {
try {db.post(animalCreate);Animal animalObject = db.find(Animal.class, animalCreate.get_id());ResponseBuilder rb = Response.created(new URI("animals/" + animalCreate.get_id())).entity(animalObject);return rb.build();
} catch (BadRequestException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.BAD_REQUEST);return Response.status(Status.BAD_REQUEST).entity(errorInfo).build();
} catch (NoDocumentException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.NOT_FOUND);return Response.status(Status.NOT_FOUND).entity(errorInfo).build();
} catch (NotAcceptableException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.METHOD_NOT_ALLOWED);return Response.status(Status.METHOD_NOT_ALLOWED).entity(errorInfo).build();
} catch (DocumentConflictException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.CONFLICT);return Response.status(Status.CONFLICT).entity(errorInfo).build();
} catch (InternalServerErrorException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.INTERNAL_SERVER_ERROR);return Response.status(Status.INTERNAL_SERVER_ERROR).entity(errorInfo).build();
} catch (ServiceUnavailableException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.SERVICE_UNAVAILABLE);return Response.status(Status.SERVICE_UNAVAILABLE).entity(errorInfo).build();
}}
119
[参考] サンプル・コード - CloudantRestClientResource.java (5)
@PUT@Path("/{_id}")@Consumes("application/json")@Produces("application/json”)public Response responsePut(@PathParam("_id") String _id, Animal animalUpdate) {
try {Animal animalObject = db.find(Animal.class, _id);if (animalUpdate.getMin_weight() != null) {
animalObject.setMin_weight(animalUpdate.getMin_weight());}if (animalUpdate.getMax_weight() != null) {
animalObject.setMax_weight(animalUpdate.getMax_weight());}if (animalUpdate.getMin_length() != null) {
animalObject.setMin_length(animalUpdate.getMin_length());}if (animalUpdate.getMax_length() != null) {
animalObject.setMax_length(animalUpdate.getMax_length());}if (animalUpdate.getLatin_name() != null) {
animalObject.setLatin_name(animalUpdate.getLatin_name());}if (animalUpdate.getWiki_page() != null) {
animalObject.setWiki_page(animalUpdate.getWiki_page());}if (animalUpdate.getDiet() != null) {
animalObject.setDiet(animalUpdate.getDiet());}db.update(animalObject);
120
[参考] サンプル・コード - CloudantRestClientResource.java (6)
Animal animalObjectUpdated = db.find(Animal.class, _id);ResponseBuilder rb = Response.ok(animalObjectUpdated).type("application/json");return rb.build();
} catch (BadRequestException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.BAD_REQUEST);return Response.status(Status.BAD_REQUEST).entity(errorInfo).build();
} catch (NoDocumentException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.NOT_FOUND);return Response.status(Status.NOT_FOUND).entity(errorInfo).build();
} catch (NotAcceptableException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.METHOD_NOT_ALLOWED);return Response.status(Status.METHOD_NOT_ALLOWED).entity(errorInfo).build();
} catch (DocumentConflictException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.CONFLICT);return Response.status(Status.CONFLICT).entity(errorInfo).build();
} catch (InternalServerErrorException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.INTERNAL_SERVER_ERROR);return Response.status(Status.INTERNAL_SERVER_ERROR).entity(errorInfo).build();
} catch (ServiceUnavailableException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.SERVICE_UNAVAILABLE);return Response.status(Status.SERVICE_UNAVAILABLE).entity(errorInfo).build();
}}
121
[参考] サンプル・コード - CloudantRestClientResource.java (7)
@DELETE@Path("/{_id}")@Produces("application/json")public Response responseDelete(@PathParam("_id") String _id) {
try {Animal animalRemove = db.find(Animal.class, _id);db.remove(animalRemove);System.out.println(animalRemove);ResponseBuilder rb = Response.noContent();return rb.build();
} catch (BadRequestException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.BAD_REQUEST);return Response.status(Status.BAD_REQUEST).entity(errorInfo).build();
} catch (NoDocumentException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.NOT_FOUND);return Response.status(Status.NOT_FOUND).entity(errorInfo).build();
} catch (NotAcceptableException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.METHOD_NOT_ALLOWED);return Response.status(Status.METHOD_NOT_ALLOWED).entity(errorInfo).build();
} catch (DocumentConflictException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.CONFLICT);return Response.status(Status.CONFLICT).entity(errorInfo).build();
} catch (InternalServerErrorException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.INTERNAL_SERVER_ERROR);return Response.status(Status.INTERNAL_SERVER_ERROR).entity(errorInfo).build();
} catch (ServiceUnavailableException e) {ErrorInfo errorInfo = createErrorInfo(e, Status.SERVICE_UNAVAILABLE);return Response.status(Status.SERVICE_UNAVAILABLE).entity(errorInfo).build();
}}}
122
[参考] サンプル・コード - CloudantRestClient.javapackage animal.web.api;import javax.ws.rs.ApplicationPath;import javax.ws.rs.core.Application;
@ApplicationPath("/")public class CloudantRestClient extends Application {}
123
[参考] サンプル・コード - ErrorInfo.javapackage animal.web.api;public class ErrorInfo {
private int statusCode;private String message;
public int getStatusCode() {return statusCode;
}public void setStatusCode(int statusCode) {this.statusCode = statusCode;
}
public String getMessage() {return message;
}public void setMessage(String message) {this.message = message;
}
}
124
[参考] サンプル・コード - Animal.java (1)package animal.web.api;
public class Animal {private String _id = null;private String _rev = null;private Double min_weight = null;private Double max_weight = null;private Double min_length = null;private Double max_length = null;private String latin_name = null;private String wiki_page = null;private String animalClass = null;private String diet = null;
public String get_id() {return _id;
}public void set_id(String _id) {this._id = _id;
}public String get_rev() {return _rev;
}
125
[参考] サンプル・コード - Animal.java (2)
public Double getMin_weight() {return min_weight;
}public void setMin_weight(double min_weight) {this.min_weight = new Double(min_weight);
}public Double getMax_weight() {return max_weight;
}public void setMax_weight(double max_weight) {this.max_weight = new Double(max_weight);
}public Double getMin_length() {return min_length;
}public void setMin_length(double min_length) {this.min_length = new Double(min_length);
}public Double getMax_length() {return max_length;
}public void setMax_length(double max_length) {this.max_length = new Double(max_length);
}
126
[参考] サンプル・コード - Animal.java (3)public String getLatin_name() {return latin_name;
}public void setLatin_name(String latin_name) {this.latin_name = latin_name;
}public String getWiki_page() {return wiki_page;
}public void setWiki_page(String wiki_page) {this.wiki_page = wiki_page;
}public String getAnimalClass() {return animalClass;
}public void setAnimalClass(String animalClass) {this.animalClass = animalClass;
}public String getDiet() {return diet;
}public void setDiet(String diet) {this.diet = diet;
}}
128
参考図書
n Web API: The Good Parts– オライリー・ジャパン 発⾏– ⽔野 貴明 著– ISBN 978-4-87311-686-0
n RESTful Web APIs– OʼReilly Media 発⾏– Leonard Richardson, Mike Amundsen, Sam Ruby 著– ISBN 978-1-4493-5806-8
129
参考⽂献 (本⽂中に挙げたものを除く)
n Google Cloud Platform: API Design Guide– https://cloud.google.com/apis/design/
n Mozilla Developer Network: Web technology for developers: HTTP– https://developer.mozilla.org/en-US/docs/Web/HTTP
n YouTube: Designing a Beautiful REST+JSON API– https://www.youtube.com/watch?v=5WXYw4J4QOU
n Rising Stack: 10 Best Practices for Writing Node.js REST APIs– https://blog.risingstack.com/10-best-practices-for-writing-node-js-rest-apis/
130
参考⽂献 (本⽂中に挙げたものを除く)
n 10 Best Practices for Writing Node.js REST APIs– https://blog.risingstack.com/10-best-practices-for-writing-node-js-rest-apis/
n Bluemix 上で稼動する Web アプリケーション開発⽅法 - Node.js 編– https://www.ibm.com/developerworks/jp/cloud/library/j_cl-bluemix-nodejs-app/
n IBM Cloudant Documentation– https://docs.cloudant.com/http.html#maincontent
n JAX-RS Web サービスの作成– https://www.ibm.com/support/knowledgecenter/ja/SSHR6W/com.ibm.websphere.wdt.d
oc/topics/core/tjaxrswebservcreate.htmn Bluemix 上で稼動する Web アプリケーション開発⽅法 - Java 編
– https://www.ibm.com/developerworks/jp/cloud/library/j_cl-bluemix-java-app/