REST APIの発見可能性とHATEOAS

REST APIの検出可能性とHATEOAS

1. 概要

この記事では、Discoverability of the REST API, HATEOASとテストによって駆動される実際のシナリオに焦点を当てます。

2. APIを検出可能にする理由

APIの発見可能性は、十分な注目を集めるに値しないトピックです。 結果として、それを正しくするAPIはほとんどありません。 また、正しく実行されれば、APIをRESTfulで使いやすくするだけでなく、エレガントにすることもできます。

発見可能性を理解するには、アプリケーション状態のエンジンとしてのハイパーメディア(HATEOAS)制約を理解する必要があります。 This constraint of a REST API is about full discoverability of actions/transitions on a Resource from Hypermedia (Hypertext really), as the only driver of application state.

対話自体、具体的にはハイパーテキストを介したAPIによって対話が駆動される場合、ドキュメントはありません。 これにより、クライアントは実際にAPIのコンテキスト外にある仮定を行うように強制されます。

結論として、ハイパーテキストのみを介したthe server should be descriptive enough to instruct the client how to use the API。 HTTP会話の場合、Linkヘッダーを介してこれを実現できます。

3. 発見可能性シナリオ(テストによる)

では、RESTサービスが検出可能になるとはどういう意味ですか?

このセクション全体を通して、JUnit、rest-assured、およびHamcrestを使用して、発見可能性の個々の特性をテストします。 the REST Service has been previously securedなので、各テストはAPIを使用する前にまずauthenticateにする必要があります。

3.1. 有効なHTTPメソッドを見つける

RESTサービスが無効なHTTPメソッドで消費された場合、応答は405 METHOD NOTALLOWEDである必要があります。

APIは、クライアントがその特定のリソースに許可されている有効なHTTPメソッドを発見するのにも役立ちます。 このため、we can use the Allow HTTP Header in the response:

@Test
public void
  whenInvalidPOSTIsSentToValidURIOfResource_thenAllowHeaderListsTheAllowedActions(){
    // Given
    String uriOfExistingResource = restTemplate.createResource();

    // When
    Response res = givenAuth().post(uriOfExistingResource);

    // Then
    String allowHeader = res.getHeader(HttpHeaders.ALLOW);
    assertThat( allowHeader, AnyOf.anyOf(
      containsString("GET"), containsString("PUT"), containsString("DELETE") ) );
}

3.2. 新しく作成されたリソースのURIを検出する

The operation of creating a new Resource should always include the URI of the newly created resource in the response.このために、LocationHTTPヘッダーを使用できます。

これで、クライアントがそのURIでGETを実行すると、リソースが利用可能になります。

@Test
public void whenResourceIsCreated_thenUriOfTheNewlyCreatedResourceIsDiscoverable() {
    // When
    Foo newResource = new Foo(randomAlphabetic(6));
    Response createResp = givenAuth().contentType("application/json")
      .body(unpersistedResource).post(getFooURL());
    String uriOfNewResource= createResp.getHeader(HttpHeaders.LOCATION);

    // Then
    Response response = givenAuth().header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
      .get(uriOfNewResource);

    Foo resourceFromServer = response.body().as(Foo.class);
    assertThat(newResource, equalTo(resourceFromServer));
}

テストは単純なシナリオに従います:creating a new Foo resource, then using the HTTP response to discover the URI where the Resource is now available。 次に、そのURIでGETを実行してリソースを取得し、元のリソースと比較します。 これは、正しく保存されたことを確認するためです。

3.3. そのタイプのすべてのリソースを取得するためのURIを見つけます

When we GET any particular Foo resource, we should be able to discover what we can do next: we can list all the available Foo resources.したがって、リソースを取得する操作では、その応答に、そのタイプのすべてのリソースを取得するURIを常に含める必要があります。

このために、Linkヘッダーを再び利用できます。

@Test
public void whenResourceIsRetrieved_thenUriToGetAllResourcesIsDiscoverable() {
    // Given
    String uriOfExistingResource = createAsUri();

    // When
    Response getResponse = givenAuth().get(uriOfExistingResource);

    // Then
    String uriToAllResources = HTTPLinkHeaderUtil
      .extractURIByRel(getResponse.getHeader("Link"), "collection");

    Response getAllResponse = givenAuth().get(uriToAllResources);
    assertThat(getAllResponse.getStatusCode(), is(200));
}

extractURIByRelの完全な低レベルコード–rel関係is shown hereによってURIを抽出する責任があることに注意してください。

このテストは、RESTのリンク関係の厄介な問題をカバーしています。すべてのリソースを取得するURIは、rel=”collection”セマンティクスを使用します。

このタイプのリンク関係はまだ標準化されていませんが、いくつかのマイクロフォーマットによってすでにin useであり、標準化が提案されています。 非標準のリンク関係を使用すると、RESTful Webサービスのマイクロフォーマットとより豊富なセマンティクスに関する議論が開かれます。

4. その他の潜在的な検出可能なURIおよびMicroformats

Other URIs could potentially be discovered via the Link headerですが、defining custom link relationsAtom Publishing Protocolmicroformatsなどのより豊富なセマンティックマークアップに移動せずに許可される既存のタイプのリンク関係はそれほど多くありません。別の記事のトピック。

たとえば、クライアントは、特定のリソースでGETを実行するときに、URIを検出して新しいリソースを作成できる必要があります。 残念ながら、モデルcreateのセマンティクスとのリンク関係はありません。

幸い、作成用のURIは、そのタイプのすべてのリソースを取得するためのURIと同じですが、唯一の違いはPOSTHTTPメソッドです。

5. 結論

how a REST API is fully discoverable from the root and with no prior knowledgeを確認しました。これは、クライアントがルートでGETを実行することでナビゲートできることを意味します。 今後、すべての状態変化は、REST APIが表現で提供する利用可能で検出可能な遷移を使用してクライアントによって駆動されます(したがって、Representational State Transfer)。

この記事では、REST Webサービスのコンテキストでの発見可能性の特徴のいくつかを取り上げ、HTTPメソッドの検出、作成と取得の関係、すべてのリソースを取得するためのURIの検出などについて説明しました。

これらすべての例とコードスニペットの実装は、over on GitHubで利用できます。 これはMavenベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。