Spring Data REST レスポンスハンドラーのオーバーライド

場合によっては、特定のリソースのカスタムハンドラーを作成する必要がある場合があります。Spring Data REST の設定、メッセージコンバーター、例外処理などを利用するには、標準の Spring MVC @Controller または @RestController の代わりに @RepositoryRestController アノテーションを使用します。@RepositoryRestController でアノテーションが付けられたコントローラーは、他のすべての RESTful エンドポイント (たとえば、/api) によって使用される RepositoryRestConfiguration.setBasePath で定義された API ベースパスから提供されます。次の例は、@RepositoryRestController アノテーションの使用方法を示しています。

@RepositoryRestController
class ScannerController {

  private final ScannerRepository repository;

  ScannerController(ScannerRepository repository) { (1)
    this.repository = repository;
  }

  @GetMapping(path = "/scanners/search/producers") (2)
  ResponseEntity<?> getProducers() {

    List<String> producers = repository.listProducers(); (3)

    // do some intermediate processing, logging, etc. with the producers

    CollectionModel<String> resources = CollectionModel.of(producers); (4)

    resources.add(linkTo(methodOn(ScannerController.class).getProducers()).withSelfRel()); (5)

    // add other links as needed

    return ResponseEntity.ok(resources); (6)
  }
}
1 この例では、コンストラクターインジェクションを使用しています。
2 このハンドラーは、カスタムハンドラーメソッドをクエリメソッドリソースとしてプラグインします。
3 このハンドラーは、基になるリポジトリを使用してデータをフェッチしますが、最終的なデータセットをクライアントに返す前に何らかの形の後処理を実行します。
4 型 T の結果は、コレクションを返すために Spring HATEOAS CollectionModel<T> オブジェクトにラップする必要があります。EntityModel<T> または RepresentationModel<T> は、それぞれ単一のアイテムに適したラッパーです。
5 この正確なメソッドへのリンクを self リンクとして追加します。
6Spring MVC の ResponseEntity ラッパーを使用してコレクションを返すと、コレクションが適切にラップされ、適切な受け入れ型でレンダリングされます。

CollectionModel はコレクション用ですが、EntityModel(またはより一般的なクラス RepresentationModel)は単一のアイテム用です。これらの型は組み合わせることができます。コレクション内の各アイテムのリンクがわかっている場合は、CollectionModel<EntityModel<String>> (または String ではなくコアドメイン型)を使用してください。そうすることで、コレクション全体だけでなく、各アイテムのリンクを組み立てることができます。

この例では、結合されたパスは RepositoryRestConfiguration.getBasePath() + /scanners/search/producers です。

集約参照の取得

PUT および POST リクエストを受信するカスタムコントローラーの場合、通常、リクエスト本文には、URI を使用して他のリソースへの参照を表現する JSON ドキュメントが含まれます。GET リクエストの場合、それらの参照はリクエストパラメーターを介して渡されます。

Spring Data REST 4.1 では、このような参照をキャプチャーし、参照先の集約の識別子、集約自体、jMolecules Association のいずれかに解決するためのハンドラーメソッドパラメーター型として AggregateReference<T, ID> が提供されています。必要なのは、その型の @RequestParam を宣言し、識別子または完全に解決された集約のいずれかを使用することだけです。

@RepositoryRestController
class ScannerController {

  private final ScannerRepository repository;

  ScannerController(ScannerRepository repository) {
    this.repository = repository;
  }

  @GetMapping(path = "/scanners")
  ResponseEntity<?> getProducers(
    @RequestParam AggregateReference<Producer, ProducerIdentifier> producer) {

    var identifier = producer.resolveRequiredId();
    // Alternatively
    var aggregate = producer.resolveRequiredAggregate();
  }

  // Alternatively

  @GetMapping(path = "/scanners")
  ResponseEntity<?> getProducers(
    @RequestParam AssociationAggregateReference<Producer, ProducerIdentifier> producer) {

    var association = producer.resolveRequiredAssociation();
  }
}

jMolecules を使用している場合、AssociationAggregateReference を使用して Association を取得することもできます。両方の抽象化では、パラメーターの値が、Spring Data REST がアイテムリソースを公開するために使用するスキームに一致する URI であると想定していますが、参照インスタンスで  … .withIdSource(…) を呼び出して、受信した URI から取得した UriComponents から最終的に集約解決に使用される識別子値を抽出する関数を提供することで、ソース値の解決をカスタマイズできます。

@RepositoryRestController VS. @BasePathAwareController

エンティティ固有の操作に関心がなくても、Spring MVC ビュー、リソースなどの basePath にカスタム操作を構築したい場合は、@BasePathAwareController を使用してください。カスタムコントローラーで @RepositoryRestController を使用している場合、リクエストマッピングがリポジトリで使用される URI スペースに融合する場合にのみ、リクエストを処理します。また、次の追加機能をコントローラーメソッドに適用します。

  1. ハンドラーメソッドのリクエストマッピングで使用されるベースパスセグメントにマッピングされたリポジトリに対して定義された CORS 構成。

  2. JPA を使用する場合は、OpenEntityManagerInViewInterceptor を適用して、遅延解決としてマークされたプロパティにアクセスできるようにします。

何かに @Controller または @RestController を使用する場合、そのコードは完全に Spring Data REST の範囲外です。これは、リクエスト処理、メッセージコンバーター、例外処理、その他の用途にまでおよびます。