好 既然 HATEOAS(Hypermedia As The Engine Of Application State) 有帶來一定程度上的好處, 那通常你必須在 Entity 中去定義其中的關連性 (OneToOne, OneToMany, ManyToOne, ManyToMany), 說實在這種關聯操作太麻煩了.....後來我幾乎都是直接原始資料對應到欄位比較多, 但是我又希望有 HATEOAS 這種描述相關資源的效果...
舉例來說 這是我們一個專案的回覆資料, 並包含 links 可以取得 project 的相關資訊,而不必關心 project url 的變動
{
"projectId":"QpbR7viWdy",
"projectName":"123",
"score":72,
"createdDate":null,
"createdBy":null,
"lastModifiedDate":"2017-07-20T09:24:28Z",
"lastModifiedBy":null,
"_links":{
"self":{
"href":"http://localhost:8080/scoreProjects/n5NehJba7k"
},
"scoreProject":{
"href":"http://localhost:8080/scoreProjects/n5NehJba7k"
},
"project":{
"href":"http://localhost:8080/projects/QpbR7viWdy"
},
"scoreProjectMembers":{
"href":"http://localhost:8080/scoreProjectMembers/search/findByProjectId?projectId=QpbR7viWdy"
},
"projectMembers":{
"href":"http://localhost:8080/api/project/QpbR7viWdy/members"
}
}
}
ScoreProject 這物件是關於專案的分數,那我們希望讓他知道專案的 url 位置讓前端自己取得
可以這樣寫,當然 Project 也必須是 RepositoryRestResource 的一個 Rest 資源
resource.add(repositoryEntityLinks.linkToSingleResource(Project.class, scoreProject.getProjectId()));
轉換出來的結果
"project": {
"href": "http://localhost:8080/projects/QpbR7viWdy"
}
那我們想利用已經定義好在 RepositoryRestResource 裡面的搜尋條件要怎麼設定
舉例說這是我們 Project 下面每一個 Member 的分數資訊
@RepositoryRestResource
public interface ScoreProjectMemberRepostiory extends JpaRepository<ScoreProjectMember, String> {
@RestResource(path = "findByProjectId", exported = true)
List<ScoreProjectMember> findByProjectId(@Param("projectId") String projectId);
}
我們可以這樣指向搜尋功能
resource.add(repositoryEntityLinks.linksToSearchResources(ScoreProjectMember.class).getLink("findByProjectId").expand(scoreProject.getProjectId()).withRel("scoreProjectMembers"));
轉換出來的結果
scoreProjectMembers": {
"href": "http://localhost:8080/scoreProjectMembers/search/findByProjectId?projectId=QpbR7viWdy"
}
或是我們自己定義好的 RestController 位置想加進去要如何寫
這是一支我們自己做的 Controller 要找出 Project 下所有 Member
@RestController
@RequestMapping("api")
public class ProjectMemberRestController {
@Autowired
private ProjectService projectService;
@ResponseStatus(HttpStatus.OK)
@GetMapping(value = "project/{projectid}/members", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public List<ProjectMember> findMemberByProjectid(
@PathVariable("projectid") String projectid) {
List<ProjectMember> projectMemberList = projectService.findMember(projectid);
return projectMemberList;
}
}
指向我們寫的 Controller
resource.add(ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(ProjectMemberRestController.class).findMemberByProjectid(scoreProject.getProjectId())).withRel("projectMembers"));
轉換出來的結果
"projectMembers": {
"href": "http://localhost:8080/api/project/QpbR7viWdy/members"
}
完整的寫法就是下面這樣子而已, spring 掃描到後就自動可以起作用了
import com.ps.controller.api.ProjectMemberRestController;
import com.ps.model.Project;
import com.ps.model.ScoreProject;
import com.ps.model.ScoreProjectMember;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.webmvc.support.RepositoryEntityLinks;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceProcessor;
import org.springframework.hateoas.mvc.ControllerLinkBuilder;
import org.springframework.stereotype.Component;
/**
* Created by samchu on 2017/7/21.
*/
@Component
public class ScoreProjectResourcesProcessor implements ResourceProcessor<Resource<ScoreProject>> {
@Autowired
private RepositoryEntityLinks repositoryEntityLinks;
@Override
public Resource<ScoreProject> process(Resource<ScoreProject> resource) {
ScoreProject scoreProject = resource.getContent();
// 指向單一物件
resource.add(repositoryEntityLinks.linkToSingleResource(Project.class, scoreProject.getProjectId()));
// "project": {
// "href": "http://localhost:8080/projects/QpbR7viWdy"
// }
// 指向搜尋功能
resource.add(repositoryEntityLinks.linksToSearchResources(ScoreProjectMember.class).getLink("findByProjectId").expand(scoreProject.getProjectId()).withRel("scoreProjectMembers"));
// scoreProjectMembers": {
// "href": "http://localhost:8080/scoreProjectMembers/search/findByProjectId?projectId=QpbR7viWdy"
// }
// 指向我們寫的 Controller
resource.add(ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(ProjectMemberRestController.class).findMemberByProjectid(scoreProject.getProjectId())).withRel("projectMembers"));
// "projectMembers": {
// "href": "http://localhost:8080/api/project/QpbR7viWdy/members"
// }
// 可以指定查詢頁數
resource.add(repositoryEntityLinks.linkToCollectionResource(Project.class).expand("5","20").withRel("projects")); // rojects?page=5&size=20
return resource;
}
}
在透過 Spring Data Rest 做任何取得或查詢的時候就都可以提供相關的資源位置給前端