Spring boot HATEOAS не добавляет автоматически ссылки на соответствующие ресурсы - PullRequest
0 голосов
/ 10 марта 2019

Ссылки не предоставляются автоматически для ресурсов при использовании 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();
    }
}
...