跳到主要内容

重写 Spring Data REST 响应处理器

DeepSeek V3 中英对照 Overriding Spring Data REST Response Handlers

有时,你可能希望为特定资源编写自定义处理器。为了充分利用 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
}
}
java
  • 此示例使用了构造函数注入。

  • 此处理程序将自定义处理程序方法作为查询方法资源插入。

  • 此处理程序使用底层存储库来获取数据,但在将最终数据集返回给客户端之前进行了一些形式的后处理。

  • 类型为 T 的结果需要包装在 Spring HATEOAS 的 CollectionModel<T> 对象中以返回集合。EntityModel<T>RepresentationModel<T> 分别适合用于包装单个项目。

  • 添加一个返回此确切方法的链接作为 self 链接。

  • 通过使用 Spring MVC 的 ResponseEntity 包装器返回集合,确保集合被正确包装并以适当的接受类型呈现。

CollectionModel 用于集合,而 EntityModel —— 或者更通用的类 RepresentationModel —— 用于单一项目。这些类型可以组合使用。如果你知道集合中每个项目的链接,可以使用 CollectionModel<EntityModel<String>>(或者用核心域类型代替 String)。这样做可以让你为每个项目以及整个集合组装链接。

important

在这个示例中,组合路径为 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();
}
}
java

如果你正在使用 jMolecules,AssociationAggregateReference 也允许你获取一个 Association。虽然这两种抽象都假设参数的值是一个 URI,该 URI 匹配 Spring Data REST 用于暴露项目资源的方案,但可以通过在引用实例上调用 ….withIdSource(…) 来自定义源值解析,以提供一个函数来提取标识符值,最终用于从接收到的 URI 中获取的 UriComponents 进行聚合解析。

@RepositoryRestController 对比 @BasePathAwareController

如果你对实体特定的操作不感兴趣,但仍然希望在 basePath 下构建自定义操作,例如 Spring MVC 视图、资源等,请使用 @BasePathAwareController。如果你在自定义控制器上使用 @RepositoryRestController,则只有当你的请求映射与存储库使用的 URI 空间融合时,它才会处理请求。它还会对控制器方法应用以下额外功能:

  1. 根据为映射到处理程序方法请求映射中使用的基础路径段的仓库定义的 CORS 配置。

  2. 如果使用 JPA,请应用 OpenEntityManagerInViewInterceptor,以确保可以访问标记为延迟加载的属性。

注意

如果你对任何内容使用了 @Controller@RestController,那么该代码完全不在 Spring Data REST 的范围内。这包括请求处理、消息转换器、异常处理以及其他用途。