コンテンツにスキップ

RESTful-API@アプリケーション連携

はじめに

本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。


01. RESTとRESTfulとは

REST

▼ RESTとは

分散型アプリケーションを連携させるのに適したアーキテクチャスタイルをRESTという。

RESTは、以下の特徴を持つ。

REST

▼ RESTfulとRESTful-APIとは

RESTに基づいた設計をRESTfulという。

RESTful設計が使用されたWebAPIをRESTful-APIという。

例えば、RESTful-APIの場合、DBにおけるUserInfoのCRUDに対して、1個の『/UserInfo』というURIを対応づけている。

RESTfulAPI


RESTの4原則

▼ Statelessであること

クライアントに対してレスポンスを返信した後に、クライアントの情報を保持せずに破棄する仕組みのこと。

擬似的にステートフルな通信を実行する時は、キャッシュ、Cookie、セッションIDを使用して、クライアントの情報を保持する。

プロトコル ステートレス/ステートフル
HTTP、HTTPS ステートレス
SSH、TLS/SSL、SFTP ステートフル

▼ Connectabilityであること

記入中...

▼ Uniform Interfaceであること

HTTPリクエストを、『リソースに対する操作』とらえ、リクエストにHTTPメソッドを対応づけるようにする。

▼ Addressabilityであること

エンドポイントによって、特定のリソースを操作できること。


02. エンドポイント

エンドポイントとは

特定のリソースを操作するための固有のURIのこと。

<IPアドレス、ドメイン>:<ポート番号>から構成される。

エンドポイント は、リソース1つごと、あるいはまとまりごとに割り振られる。


HTTPメソッド、エンドポイント、ユースケースの関係

RESTfulAPIでは、全てのHTTPメソッドの内、主に以下の4個を使用して、データ処理の方法をリクエストする。

それぞれが、APIのユースケースに対応する。

ユースケースごとのメソッド名については、Laravelを参考にする。

HTTPメソッド エンドポイント ユースケース メソッド名の例
GET https://example.com/users ・全データのインデックス取得
・条件に基づくデータの取得
index
https://example.com/users/{id} IDに基づくデータの取得 show
POST https://example.com/users ・データの作成
・PDFの作成
・ファイルデータの送信
・ログイン/ログアウト
create、store
PUT https://example.com/users/{id} データの更新 (置換) update
DELETE https://example.com/users/{id} データの削除 delete、destroy

POST送信とPUT送信の重要な違いについてまとめる。

データを作成するユースケースの時はPOST送信、または更新する時はPUT送信を使用する。

ただしもっと正確には、ユースケースが『作成』or『更新』ではなく、『非冪等』or『冪等』で判断したほうが良い。

POST送信 PUT送信
データ作成の冪等性 リクエスト1つにつき、1個のデータを作成 (非冪等的) リクエスト数に限らず、1個のデータを作成する (冪等的) 。古いデータを新しいデータに置換する行為に近い。
リクエストパラメーターの場所 メッセージボディにJSON型データなどを割り当てる。 パスパラメーターにidなど、またメッセージボディにJSON型データなどを割り当てる。


エンドポイントの命名

▼ 動詞を使用しないこと

すでにHTTPメソッド自体に動詞の意味合いが含まれるため、エンドポイントに動詞を含めないようにする。

この時、アクセスするリソース名がわかりやすいような名詞を使用する。

ただし慣例として、認証のエンドポイントが動詞 (loginlogoutregister) になることは許容されている。

*悪い実装例*

GET https://example.com/show-user/12345

*良い実装例*

GET https://example.com/users/12345
GET https://example.com/users/foo

*認証の場合*

動詞を許容するのであればloginlogoutとし、名詞を採用するのであればsessionとする。

GET https://example.com/login
GET https://example.com/session

▼ 短くすること

*悪い実装例*

ここで、serviceapi、といったキーワードは、なくても問題ない。

GET https://example.com/service/api/users/12345

*良い実装例*

GET https://example.com/users/12345

▼ 略称を使用しないこと

*悪い実装例*

ここで、Usersを意味する『u』といった略称は、当時の設計者しかわからないため、不要である。

GET https://example.com/u/12345

*良い実装例*

略称を使わずに、『users』とする。

GET https://example.com/users/12345

▼ 小文字を使用すること

*悪い実装例*

GET https://example.com/Users/12345

*良い実装例*

GET https://example.com/users/12345

▼ ケバブケースを使用すること

*悪い実装例*

GET https://example.com/users_id/12345

*良い実装例*

スネークケースやキャメケースを使わずに、ケバブケースを使用する。

GET https://example.com/users-id/12345

ただし、そもそもケバブ方式も利用せずに、スラッシュで区切ってしまうのも手である

GET https://example.com/users/id/12345

▼ 複数形を使用すること

*悪い実装例*

Usersという集合の中に、Idが存在しているため、単数形は使用しない。

GET https://example.com/user/12345

*良い実装例*

GET https://example.com/users/12345

▼ システムの設計方法がバレないURIにすること

*悪い実装例*

悪意のあるユーザーに、脆弱性を狙われる可能性があるため、ソフトウェアの設計方法がばれないアーキテクチャにすること。

ミドルウェアにCGIプログラムが使用されていることや、phpを使用していることがばれてしまう。

GET https://example.com/cgi-bin/get_users.php

*良い実装例*

GET https://example.com/users/12345

▼ HTTPメソッドの名前を使用しないこと

*悪い実装例*

メソッドから、処理の目的はわかるため、URIに対応する動詞名を実装する必要はない。

GET https://example.com/users/get/12345
POST https://example.com/users/create/12345
PUT https://example.com/users/update/12345
DELETE https://example.com/users/delete/12345

*良い実装例*

GET https://example.com/users/{id}
POST https://example.com/users
PUT https://example.com/users/{id}
DELETE https://example.com/users/{id}

▼ 数字、バージョン番号をできる限り使用しないこと

*悪い実装例*

ここで、alphav2、といったキーワードは、当時の設計者しかわからないため、適さない。

ただし、利便上、使用する場合もある。

GET https://example.com/v2/users/12345

*良い実装例*

GET https://example.com/users/12345

URLにバージョンを表記しない代わりとして、リクエストヘッダーのX-api-Versionにバージョン情報を格納する方法がより良い。

X-Api-Version: 1

▼ 異なるHTTPメソッドの間でルールを統一すること

*悪い実装例*

GET送信とPOST送信の間で、IDパラメーターのHTTPメソッドが統一されていない。

GET https://example.com/users/?id=12345
POST https://example.com/users/12345/messages

*良い実装例*

以下の様に、異なるHTTPメソッドの間でも統一する。

GET https://example.com/users/12345
POST https://example.com/users/12345/messages


エンドポイントのパラメーター

▼ パス、クエリストリングへの割り当て

URIの構造のうち、パスまたはクエリストリングにパラメーターを割り当てて送信する。

それぞれ、パスパラメーターまたはクエリパラメーターという。

GET https://example.com:80/users/777?text1=a&text2=b
完全修飾ドメイン名 宛先のポート番号 (80の場合は省略可) ルート パスパラメーター クエリパラメーター (GET送信時のみ)
https://example.com 80 users {id} ? text1=a&text2=b

▼ 使い分け (再掲)

データの宛先 パスパラメーター クエリパラメーター
単一条件で決まる検索処理
複数条件で決まる検索処理
フィルタリング処理
ソーティング処理

▼ メッセージボディへの割り当て

JSON型データ内に定義し、メッセージボディにパラメーターを割り当てて送信する。

POST https://example.com
---
# ボディ
{"id": 1, "name": "foo"}

▼ リクエストヘッダーへの割り当て

リクエストヘッダーにパラメーターを割り当てて送信する。

送信時のヘッダー名は大文字でも小文字でもいずれでも問題ないが、内部的に小文字に変換されるため、小文字が推奨である。

APIキーのヘッダー名の頭文字に『X』を付けるのは、自前ヘッダーの頭文字に『X』を付ける慣習があったためである。

ただし、現在は非推奨である。

POST https://example.com
---
# Authorizationヘッダー
authorization: Bearer <トークン>
# APIキーヘッダー
x-api-key: *****


03. レスポンス

正常系レスポンスの場合

▼ POST/PUTでは処理後データをレスポンス

POST/PUTメソッドでは、処理後のデータを200レスポンスとして返信する。

もし処理後のデータを返信しない場合、改めてGETリクエストを送信する必要があり、余分なAPIコールが必要になってしまう。

▼ DELETEではメッセージのみをレスポンス

DELETEメソッドでは、メッセージのみを200レスポンスとして返信する。

空ボディ204レスポンスとして返信しても良い。

▼ ステータスコードは不要

正常系レスポンスの場合、オブジェクトデータへのステータスコードの割り当ては不要である。

{"name": "Taro Yamada"}

▼ フラットなデータ構造にすること

JSONの場合、階層構造にすると、データサイズが増えてしまう。

*例*

{
  "name": "Taro Yamada",
  "age": 10,
  "interest": {"sports": ["soccer", "baseball"], "subjects": "math"},
}

そこで、できるだけデータ構造をフラットにする。

ただし、見やすさによっては階層構造も許容される。

*例*

{
  "name": "Taro Yamada",
  "age": 10,
  "sports": ["soccer", "baseball"],
  "subjects": "math",
}

代わりとして、Content-Typeヘッダーに『application/hal+json』『application/vnd.api+json』『application/vnd.collection+json』といったよりJSONベースの強い制約のフォーマットを利用しても良い。

▼ 日付データの形式に気をつけること

RFC3339 (W3C-DTF) 形式でオブジェクトデータに含めて送受信すること。

*例*

2020-07-07T12:00:00+09:00

ただし、日付をリクエストパラメーターで送受信する時、RFC3339 (W3C-DTF) 形式を正規表現で設定する必要があるので注意。

*例*

GET https://example.com/users/12345?date=2020-07-07T12:00:00%2B09:00


異常系レスポンスの場合

項目名 必要性 データ型 説明
エラーメッセージ 必須 string型 複数のエラーメッセージを返信できるように、配列として定義する。
ステータスコード 任意 integer型 エラーの種類がわかるステータスコードを割り当てる。
エラーコード (例外コード) 任意 string型 APIドキュメントのエラーの識別子として、エラコード (例外コード) を割り当てる。
APIドキュメントのURL 任意 string型 外部に公開するAPIの場合、エラーの解決策がわかるAPIドキュメントのURLを割り当てる。
{
  "code": 400,
  "errors": ["〇〇は必ず入力してください。", "□□は必ず入力してください。"],
  "url": "https://foo-api-doc.co.jp",
}