Ссылки не предоставляются автоматически для ресурсов при использовании HATEOAS для получения коллекций ресурсов.
При извлечении коллекции ThreadResource с помощью '/ forum / threads', ответ будет таким:
{
"_embedded": {
"threadList": [
{
"posts": [
{
"postText": "This text represents a major breakthrough in textual technology.",
"thread": null,
"comments": [],
"thisId": 1
},
{
"postText": "This text represents a major breakthrough in textual technology.",
"thread": null,
"comments": [],
"thisId": 2
}
],
"createdBy": "admin",
"updatedBy": null,
"thisId": 1
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/forum/threads?page=0&size=10"
}
},
"page": {
"size": 10,
"totalElements": 1,
"totalPages": 1,
"number": 0
}
}
Я ожидал массив сообщений JSON (вместо ссылок на коллекцию связанных сообщений), как показано ниже:
{
"_embedded": {
"threadList": [
{
"createdBy": "admin",
"updatedBy": null,
"thisId": 1,
"_links": {
"posts": {
"href": "http://localhost:8080/forum/threads/1/posts"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/forum/threads?page=0&size=10"
}
},
"page": {
"size": 10,
"totalElements": 1,
"totalPages": 1,
"number": 0
}
}
Я мог бы вручную создавать и добавлять ссылки в классы реализации ResourceProcessor и исключать сборку из списка при помощи @JsonIgnore, но мне никогда не приходилось делать это раньше. Что я делаю не так?
Соответствующие классы представлены ниже. Заранее спасибо!
@Entity
public class Thread {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany
private List<Post> posts;
@Column(name = "created_by")
private String createdBy;
@Column(name = "updated_by")
private String updatedBy;
public Thread() { }
@PrePersist
public void prePersist() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
posts = new ArrayList<>();
createdBy = auth.getName();
}
@PreUpdate
public void preUpdate() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
updatedBy = auth.getName();
}
public void submitPost(Post newPost) {
posts.add(newPost);
}
public Long getThisId() {
return id;
}
public List<Post> getPosts() {
return posts;
}
public void setPosts(List<Post> posts) {
this.posts = posts;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public String getUpdatedBy() {
return updatedBy;
}
public void setUpdatedBy(String updatedBy) {
this.updatedBy = updatedBy;
}
@Entity
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String postText;
@ManyToOne(fetch = FetchType.LAZY)
private Thread thread;
@OneToMany
private List<Comment> comments;
public Post() { }
}
public class ThreadResource extends ResourceSupport {
private List<PostResource> postResources;
private String createdBy;
private String updatedBy;
public ThreadResource() {
}
}
public class PostResource extends ResourceSupport {
private String postText;
private ThreadResource threadResource;
private List<CommentResource> commentResources;
public PostResource() { }
@Component
public class PostResourceAssembler extends ResourceAssemblerSupport<Post, PostResource> {
public PostResourceAssembler() {
super(PostController.class, PostResource.class);
}
@Override
public PostResource toResource(Post entity) {
PostResource resource = super.createResourceWithId(entity.getThisId(), entity);
resource.setPostText(entity.getPostText());
return resource;
}
}
@Component
public class ThreadResourceAssembler extends ResourceAssemblerSupport<Thread, ThreadResource> {
private PostResourceAssembler postResourceAssembler;
public ThreadResourceAssembler(PostResourceAssembler postResourceAssembler) {
super(ThreadController.class, ThreadResource.class);
this.postResourceAssembler = postResourceAssembler;
}
@Override
public ThreadResource toResource(Thread entity) {
ThreadResource resource = super.createResourceWithId(entity.getThisId(), entity);
List<Post> posts = entity.getPosts();
List<PostResource> postResources = new ArrayList<>();
posts.forEach((post) -> postResources.add(postResourceAssembler.toResource(post)));
resource.setPostResources(postResources);
return resource;
}
}
@RestController
public class PostController {
private PostService postService;
@Autowired
public PostController(PostService postService) {
this.postService = postService;
}
@GetMapping("/forum/threads/{threadId}/posts/{postId}")
public ResponseEntity<Resource<Post>> getPost(@PathVariable long threadId, @PathVariable long postId) {
Post post = postService.fetchPost(postId)
.orElseThrow(() -> new EntityNotFoundException("not found thread " + postId));
Link selfLink = linkTo(PostController.class).slash(postId).withSelfRel();
post.add(selfLink);
return ResponseEntity.ok(new Resource<>(post));
}
@GetMapping
public ResponseEntity<PagedResources<Resource<Post>>> getPosts(PagedResourcesAssembler<Post> pagedResourcesAssembler) {
Pageable pageable = new PageRequest(0, 10);
Page<Post> posts = postService.fetchAllPosts(pageable);
PagedResources<Resource<Post>> resources = pagedResourcesAssembler.toResource(posts);
return ResponseEntity.ok(resources);
}
@PostMapping("/forum/threads/{threadId}/posts")
public HttpEntity<?> submitPost(@PathVariable long threadId) throws URISyntaxException {
Post post = postService.submitPost(threadId, new Post());
if (post != null) {
Link selfLink = linkTo(methodOn(PostController.class).submitPost(threadId)).slash(post.getThisId()).withSelfRel();
post.add(selfLink);
return ResponseEntity.created(new URI(selfLink.getHref())).build();
}
return ResponseEntity.status(500).build();
}
}
@RestController
public class ThreadController {
private ThreadService threadService;
private ThreadResourceAssembler threadResourceAssembler;
@Autowired
public ThreadController(ThreadService threadService,
ThreadResourceAssembler threadResourceAssembler) {
this.threadService = threadService;
this.threadResourceAssembler = threadResourceAssembler;
}
@GetMapping("/forum/threads/{threadId}")
public ResponseEntity<ThreadResource> getThread(@PathVariable long threadId) {
Thread thread = threadService.fetchThread(threadId)
.orElseThrow(() -> new EntityNotFoundException("not found thread " + threadId));
ThreadResource threadResource = threadResourceAssembler.toResource(thread);
return ResponseEntity.ok(threadResource);
}
@GetMapping("/forum/threads")
public ResponseEntity<PagedResources<Resource<ThreadResource>>> getThreads(PagedResourcesAssembler pagedResourcesAssembler) {
Pageable pageable = new PageRequest(0, 10);
Page<Thread> threads = threadService.fetchAllThreads(pageable);
PagedResources pagedResources = pagedResourcesAssembler.toResource(threads);
return ResponseEntity.ok(pagedResources);
}
@PostMapping("/forum/threads")
public HttpEntity<?> createThread() {
Thread thread = threadService.createThread();
return ResponseEntity.ok(thread);
}
@DeleteMapping("/forum/threads/{threadId}")
public HttpEntity<?> deleteThread(@PathVariable long threadId) {
Thread thread = threadService.fetchThread(threadId)
.orElseThrow(() -> new EntityNotFoundException("not found thread" + threadId));
threadService.closeThread(thread);
return ResponseEntity.ok().build();
}
}