1. 概要

この記事では、 REST APIの検出可能性、HATEOAS 、およびテストによって駆動される実際のシナリオに焦点を当てます。

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

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

検出可能性を理解するには、アプリケーション状態のエンジンとしてのハイパーメディア(HATEOAS)制約を理解する必要があります。 REST APIのこの制約は、ハイパーメディア(実際にはハイパーテキスト)からのリソースに対するアクション/遷移の完全な検出可能性に関するものです。アプリケーション状態の唯一のドライバー。

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

結論として、サーバーは、ハイパーテキストのみを介してAPIを使用する方法をクライアントに指示するのに十分な説明が必要です。 HTTP会話の場合、Linkヘッダーを介してこれを実現できます。

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

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

このセクション全体を通して、Junit、 rest-assured 、および Hamcrest を使用して、発見可能性の個々の特性をテストします。 RESTサービスは以前に保護されているため、各テストはAPIを使用する前にまず認証する必要があります。

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

RESTサービスが無効なHTTPメソッドで使用されている場合、応答は405 METHODNOTALLOWEDである必要があります。

APIは、クライアントがその特定のリソースで許可されている有効なHTTPメソッドを検出するのにも役立ちます。 このために、応答でAllow HTTPヘッダーを使用できます:

@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を検出する

新しいリソースを作成する操作では、応答に新しく作成されたリソースのURIを常に含める必要があります。 このために、私たちは使用することができます位置 HTTPヘッダー。

これで、クライアントがその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));
}

テストは単純なシナリオに従います。新しいFooリソースを作成し、HTTP応答を使用して、リソースが現在利用可能なURIを検出します。 次に、そのURIに対してGETを実行してリソースを取得し、元のURIと比較します。 これは、正しく保存されたことを確認するためです。

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

特定のFooリソースを取得すると、次に何ができるかを見つけることができるはずです。利用可能なすべてのFooリソースを一覧表示できます。したがって、リソースを取得する操作は、常にその応答にそのタイプのすべてのリソースを取得する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関係によるURIの抽出を担当するものがここに示されていることに注意してください

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

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

4. その他の潜在的な検出可能なURIとMicroformats

他のURIは、リンクヘッダーを介して検出される可能性がありますが、カスタムリンクリレーションの定義などのより豊富なセマンティックマークアップに移行せずに、既存のタイプのリンクリレーションで許可されるものは限られています。 Atom PublishingProtocolまたはmicroformats。これは別の記事のトピックになります。

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

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

5. 結論

REST APIがルートから完全に検出可能であり、事前の知識がなくても、つまりクライアントがルートでGETを実行することでRESTAPIをナビゲートできることを確認しました。 今後、すべての状態変更は、REST APIが表現で提供する利用可能で検出可能な遷移を使用してクライアントによって駆動されます(したがって、 Representational State Transfer )。

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

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