跳到主要内容

自定义 Spring Data REST

DeepSeek V3 中英对照 Customizing Spring Data REST

有许多选项可以定制 Spring Data REST。以下小节将展示如何操作。

自定义项目资源 URI

默认情况下,项目资源的 URI 由集合资源的路径段与数据库标识符拼接而成。这样你就可以使用仓库的 findOne(…) 方法来查找实体实例。从 Spring Data REST 2.5 开始,可以通过在 RepositoryRestConfiguration 上使用配置 API(推荐在 Java 8 上使用)或在应用程序中注册 EntityLookup 的实现作为 Spring bean 来自定义此行为。Spring Data REST 会识别这些配置,并根据其实现调整 URI 的生成方式。

假设有一个 User 类,其中包含一个唯一标识它的 username 属性。进一步假设在相应的仓库中有一个 Optional<User> findByUsername(String username) 方法。

在 Java 8 中,我们可以将映射方法注册为方法引用,以调整 URI 的创建,如下所示:

@Component
public class SpringDataRestCustomization implements RepositoryRestConfigurer {

@Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.withEntityLookup()
.forRepository(UserRepository.class)
.withIdMapping(User::getUsername)
.withLookup(UserRepository::findByUsername);
}
}
java

forRepository(…) 将仓库类型作为第一个参数,将一个将仓库的领域类型映射到某个目标类型的方法引用作为第二个参数,并使用第一个参数中提到的仓库来将该值映射回去的另一个方法引用作为第三个参数。

如果你没有运行 Java 8 或更高版本,你可以使用该方法,但它需要一些相当冗长的匿名内部类。在较旧的 Java 版本中,你可能更倾向于实现一个类似于以下的 UserEntityLookup

@Component
public class UserEntityLookup extends EntityLookupSupport<User> {

private final UserRepository repository;

public UserEntityLookup(UserRepository repository) {
this.repository = repository;
}

@Override
public Serializable getResourceIdentifier(User entity) {
return entity.getUsername();
}

@Override
public Object lookupEntity(Serializable id) {
return repository.findByUsername(id.toString());
}
}
java

注意 getResourceIdentifier(…) 方法返回了用于创建 URI 的用户名。为了根据该方法返回的值加载实体实例,我们现在通过使用 UserRepository 上可用的查询方法来实现 lookupEntity(…)

自定义仓库暴露

默认情况下,所有公开的 Spring Data 仓库都会按照仓库资源中描述的方式暴露为 HTTP 资源。包级受保护的仓库接口会被排除在外,因为您表达了其功能仅对内部包可见。这可以通过在 RepositoryRestConfiguration 上显式设置 RepositoryDetectionStrategy(通常通过枚举 RepositoryDetectionStrategies)来自定义。可以配置以下值:

  • ALL — 暴露所有的 Spring Data 仓库,无论它们的 Java 可见性或注解配置如何。

  • DEFAULT — 暴露公共的 Spring Data 仓库,或者明确使用 @RepositoryRestResource 注解且其 exported 属性未设置为 false 的仓库。

  • VISIBILITY — 仅暴露公共的 Spring Data 仓库,无论注解配置如何。

  • ANNOTATED — 仅暴露明确使用 @RepositoryRestResource 注解且其 exported 属性未设置为 false 的 Spring Data 仓库。

如果你需要应用自定义规则,只需手动实现 RepositoryDetectionStrategy 即可。

自定义支持的 HTTP 方法

自定义默认暴露

默认情况下,Spring Data REST 会根据仓库暴露的 CRUD 方法,按照仓库资源中描述的方式暴露 HTTP 资源和方法。仓库不需要继承 CrudRepository,也可以选择性地声明前述部分中描述的方法,资源暴露将随之调整。例如,如果仓库没有暴露 delete(…) 方法,则不会支持对项目资源的 HTTP DELETE 请求。

如果你需要声明一个方法供内部使用,但不希望它触发 HTTP 方法的暴露,可以在仓库方法上使用 @RestResource(exported = false) 注解。要了解如何通过这种方式移除对哪些 HTTP 方法的支持,请参阅 Repository resources 文档。

有时在方法级别管理暴露程度可能不够精细。例如,save(…) 方法既用于处理集合资源的 POST 请求,也用于处理项目资源的 PUTPATCH 请求。为了有选择地定义哪些 HTTP 方法应该被暴露,你可以使用 RepositoryRestConfiguration.getExposureConfiguration()

该类提供了一个基于 Lambda 的 API 来定义全局和基于类型的规则:

ExposureConfiguration config = repositoryRestConfiguration.getExposureConfiguration();

config.forDomainType(User.class).disablePutForCreation(); 1
config.withItemExposure((metadata, httpMethods) -> httpMethods.disable(HttpMethod.PATCH)); 2
java
  • 禁用通过 HTTP PUT 直接创建项目资源的功能。

  • 禁用对所有项目资源使用 HTTP PATCH 的功能。

章节摘要

📄️ 覆盖 Spring Data REST 的响应处理器

有时,您可能希望为特定资源编写自定义处理程序。为了利用 Spring Data REST 的设置、消息转换器、异常处理等功能,请使用 @RepositoryRestController 注解,而不是标准的 Spring MVC @Controller 或 @RestController。使用 @RepositoryRestController 注解的控制器将从 RepositoryRestConfiguration.setBasePath 定义的 API 基础路径提供服务,该路径由所有其他 RESTful 端点使用(例如 /api)。以下示例展示了如何使用 @RepositoryRestController 注解:

📄️ 向 Jackson 的 ObjectMapper 添加自定义序列化和反序列化器

有时,Spring Data REST 的 ObjectMapper(它经过特殊配置,使用了智能序列化器,可以将领域对象转换为链接并再次还原)可能无法正确处理你的领域模型。数据的结构方式多种多样,你可能会发现自己的领域模型无法正确地转换为 JSON 格式。在这些情况下,支持复杂领域模型的通用方式有时并不切实际。根据复杂程度的不同,有时甚至无法提供一个通用的解决方案。