自定义 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);
}
}
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());
}
}
注意 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
请求,也用于处理项目资源的 PUT
和 PATCH
请求。为了有选择地定义哪些 HTTP 方法应该被暴露,你可以使用 RepositoryRestConfiguration.getExposureConfiguration()
。
该类提供了一个基于 Lambda 的 API 来定义全局和基于类型的规则:
ExposureConfiguration config = repositoryRestConfiguration.getExposureConfiguration();
config.forDomainType(User.class).disablePutForCreation(); 1
config.withItemExposure((metadata, httpMethods) -> httpMethods.disable(HttpMethod.PATCH)); 2
禁用通过 HTTP
PUT
直接创建项目资源的功能。禁用对所有项目资源使用 HTTP
PATCH
的功能。
章节摘要
📄️ 配置 REST URL 路径
你可以配置 URL 路径的段,以便在这些段下导出 JPA 仓库的资源。为此,你可以在类级别或查询方法级别添加注解。
📄️ 在现有的 Spring MVC 应用中添加 Spring Data REST
你可以将 Spring Data REST 集成到现有的 Spring MVC 应用程序中。在你的 Spring MVC 配置中(通常是在配置 MVC 资源的地方),添加一个对负责配置 RepositoryRestController 的 Java 配置类的 bean 引用。该类的名称为 org.springframework.data.rest.webmvc.RepositoryRestMvcConfiguration。以下示例展示了如何使用 @Import 注解来添加正确的引用:
📄️ 覆盖 Spring Data REST 的响应处理器
有时,您可能希望为特定资源编写自定义处理程序。为了利用 Spring Data REST 的设置、消息转换器、异常处理等功能,请使用 @RepositoryRestController 注解,而不是标准的 Spring MVC @Controller 或 @RestController。使用 @RepositoryRestController 注解的控制器将从 RepositoryRestConfiguration.setBasePath 定义的 API 基础路径提供服务,该路径由所有其他 RESTful 端点使用(例如 /api)。以下示例展示了如何使用 @RepositoryRestController 注解:
📄️ 自定义 JSON 输出
有时在你的应用程序中,你需要为特定实体提供指向其他资源的链接。例如,客户响应可能会被丰富化,包含指向当前购物车的链接或管理与该实体相关资源的链接。Spring Data REST 提供了与 Spring HATEOAS 的集成,并提供了一个扩展钩子,允许你修改发送到客户端的资源表示形式。
📄️ 向 Jackson 的 ObjectMapper 添加自定义序列化和反序列化器
有时,Spring Data REST 的 ObjectMapper(它经过特殊配置,使用了智能序列化器,可以将领域对象转换为链接并再次还原)可能无法正确处理你的领域模型。数据的结构方式多种多样,你可能会发现自己的领域模型无法正确地转换为 JSON 格式。在这些情况下,支持复杂领域模型的通用方式有时并不切实际。根据复杂程度的不同,有时甚至无法提供一个通用的解决方案。
📄️ 配置 CORS
出于安全原因,浏览器禁止对当前源之外的资源进行 AJAX 调用。当处理浏览器发起的客户端 HTTP 请求时,你可能希望启用对特定 HTTP 资源的访问。