覆蓋 Spring Data REST 響應處理器

有時,你可能希望為特定資源編寫一個自定義處理器。為了利用 Spring Data REST 的設定、訊息轉換器、異常處理等功能,請使用 @RepositoryRestController 註解而不是標準的 Spring MVC @Controller@RestController。使用 @RepositoryRestController 註解的控制器會使用 RepositoryRestConfiguration.setBasePath 中定義的 API 基礎路徑(所有其他 RESTful 端點也使用此路徑,例如 /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 此處理器使用底層 repository 獲取資料,但在向客戶端返回最終資料集之前會進行某種形式的後處理。
4 型別 T 的結果需要包裝在 Spring HATEOAS 的 CollectionModel<T> 物件中以返回集合。EntityModel<T>RepresentationModel<T> 分別是單個專案的合適包裝器。
5 新增一個連結指向該方法本身,作為 self 連結。
6 透過使用 Spring MVC 的 ResponseEntity 包裝器返回集合,可以確保集合被正確包裝並以適當的 Accept 型別呈現。

CollectionModel 用於表示集合,而 EntityModel(或更通用的類 RepresentationModel)用於表示單個專案。這些型別可以組合使用。如果您知道集合中每個專案的連結,可以使用 CollectionModel<EntityModel<String>>(或相應的核心領域型別,而不是 String)。這樣做可以讓你為每個專案以及整個集合組裝連結。

在此示例中,組合路徑是 RepositoryRestConfiguration.getBasePath() + /scanners/search/producers

獲取聚合引用

對於接收 PUTPOST 請求的自定義控制器,請求體通常包含一個 JSON 文件,該文件將使用 URI 來表示對其他資源的引用。對於 GET 請求,這些引用透過請求引數傳遞。

從 Spring Data REST 4.1 開始,我們提供了 AggregateReference<T, ID>,可作為處理器方法的引數型別,用於捕獲此類引用,並將其解析為引用的聚合的識別符號、聚合本身或 jMolecules 的 Association。你只需宣告一個該型別的 @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。雖然這兩個抽象都假設引數的值是一個 URI,並且該 URI 匹配 Spring Data REST 用來暴露專案資源的模式,但透過在引用例項上呼叫 ….withIdSource(…) 可以定製源值的解析方式,以提供一個函式從接收到的 URI 中獲得的 UriComponents 中提取最終用於聚合解析的識別符號值。

@RepositoryRestController 對比 @BasePathAwareController

如果你對特定於實體的操作不感興趣,但仍想在 basePath 下構建自定義操作,例如 Spring MVC 檢視、資源等,請使用 @BasePathAwareController。如果你在自定義控制器上使用 @RepositoryRestController,它只會在你的請求對映與 repository 使用的 URI 空間融合時處理請求。它還會對控制器方法應用以下額外功能:

  1. 根據 handler 方法請求對映中使用的基礎路徑段所對映的 repository 定義的 CORS 配置。

  2. 如果使用 JPA,應用一個 OpenEntityManagerInViewInterceptor,以確保你可以訪問標記為延遲解析的屬性。

如果你使用 @Controller@RestController 執行任何操作,那麼這段程式碼完全不在 Spring Data REST 的範圍之內。這包括請求處理、訊息轉換器、異常處理以及其他用途。