如何配置Spring Data REST返回带模板化ID的链接如users/{id}
我有一个针对User实体的Spring Data仓库,Spring Data REST在根路径返回的HAL JSON如下:
{ "_links": { "users": { "href": "http://localhost:8080/users" } } }
我希望客户端能知晓,可以通过http://localhost:8080/users/{id}(比如http://localhost:8080/users/25)获取特定用户,期望Spring Data REST返回这样的格式:
{ "_links": { "users": { "href": "http://localhost:8080/users/{id}", "templated": true } } }
更新1
我想要实现类似GitHub API的效果——API根路径明确标识/users/{user}是可用接口,比如/users/odrotbohm是有效请求地址。怎么让Spring Data REST也把http://localhost:8080/users/{id}标识为API的一部分?
更新2
CrudRepository会提供http://localhost:8080/users/{id}这类模板化链接吗?我没看到。我用的是JavaScript Traverson HAL客户端,缺少这个链接的话没法遍历到特定用户。
更新3
以下是完整的示例代码:
build.gradle
plugins { id 'java' id 'org.springframework.boot' version '3.1.2' id 'io.spring.dependency-management' version '1.1.2' } group = 'com.example' version = '1.0' java { sourceCompatibility = '17' } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-data-rest' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' testImplementation 'org.springframework.boot:spring-boot-starter-test' } tasks.named('test') { useJUnitPlatform() }
项目文件
TaversonIssueApplication.java
@SpringBootApplication public class TaversonIssueApplication { public static void main(String[] args) { SpringApplication.run(TaversonIssueApplication.class, args); } }
User.java
@Entity @Table(name="`User`") @Data public class User { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; private String name; }
UserRepository.java
public interface UserRepository extends CrudRepository<User, Long> { }
初始化数据(src/main/resources)
INSERT INTO "user" (name) VALUES ('bob'); INSERT INTO "user" (name) VALUES ('sally');
启动应用后,请求http://localhost:8080/得到的响应是:
{ "_links" : { "users" : { "href" : "http://localhost:8080/users" }, "profile" : { "href" : "http://localhost:8080/profile" } } }
客户端没法从这个响应知道http://localhost:8080/users/1是有效链接,Traverson HAL客户端也没法从根路径遍历到该地址,核心问题就是缺少http://localhost:8080/users/{id}这种模板化链接。
有没有办法让Spring Data REST在根路径响应中返回这样的模板化链接?
方法1:自定义资源处理器(ResourceProcessor)
通过实现RepresentationModelProcessor<RepositoryLinksResource>来修改根路径的链接,添加模板化的单用户链接:
import org.springframework.data.rest.webmvc.RepositoryLinksResource; import org.springframework.hateoas.Link; import org.springframework.hateoas.server.RepresentationModelProcessor; import org.springframework.stereotype.Component; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; @Component public class RootResourceProcessor implements RepresentationModelProcessor<RepositoryLinksResource> { @Override public RepositoryLinksResource process(RepositoryLinksResource resource) { // 动态获取当前上下文的Base URL,避免硬编码 String baseUrl = ServletUriComponentsBuilder.fromCurrentContextPath().build().toUriString(); // 添加模板化单用户链接,标记为templated=true Link userTemplateLink = Link.of(baseUrl + "/users/{id}", "user") .withTemplated(true); resource.add(userTemplateLink); return resource; } }
这样根路径响应会新增一个user链接,格式完全符合需求。
方法2:利用Spring Data REST原生的Profile端点
Spring Data REST默认提供的/profile端点包含实体的完整API元数据,其中就有单实体的模板化链接。引导客户端访问/profile/users,会得到如下响应:
{ "_links": { "self": { "href": "http://localhost:8080/profile/users" }, "collection": { "href": "http://localhost:8080/users" }, "item": { "href": "http://localhost:8080/users/{id}", "templated": true } } }
这是Spring Data REST原生支持的方案,不需要额外编码,适合能兼容先查询profile信息的客户端。
方法3:自定义根控制器
直接编写控制器覆盖默认根路径响应,手动添加所有必要链接:
import org.springframework.hateoas.Link; import org.springframework.hateoas.RepresentationModel; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; @RestController public class CustomRootController { @GetMapping("/") public RepresentationModel<?> root() { RepresentationModel<?> model = new RepresentationModel<>(); String baseUrl = ServletUriComponentsBuilder.fromCurrentContextPath().build().toUriString(); // 添加集合链接 model.add(Link.of(baseUrl + "/users", "users")); // 添加模板化单用户链接 model.add(Link.of(baseUrl + "/users/{id}", "user").withTemplated(true)); // 添加默认的profile链接 model.add(Link.of(baseUrl + "/profile", "profile")); return model; } }
注意这种方式会完全替换Spring Data REST的默认根路径响应,需要确保包含所有业务需要的链接。
内容的提问来源于stack exchange,提问作者James

