【问题标题】:Get _links with spring RepositoryRestController使用 spring RepositoryRestController 获取 _links
【发布时间】:2017-09-06 12:13:16
【问题描述】:

我已经为我的存储库定义了自定义控制器。例如,一个看起来像这样

@RequestMapping(method = RequestMethod.GET, value = "/myEntities")
public ResponseEntity<?> get() {
    ...TO SOME STUFF
    MyEntity myEntity= myEntityRepository.findById(1);
    return ResponseEntity.ok(new Resource<>(myEntity));
}

这将返回一个 JSON 格式的数据,其中包括一个 _links 部分,我可以在其中获取实体的 href。 现在,如果我想返回一个都是资源的实体数组,我会卡住。

到目前为止我所尝试的:

1.

@RequestMapping(method = RequestMethod.GET, value = "/myEntities")
public ResponseEntity<?> get() {
    ...TO SOME STUFF
    List<MyEntity> myEntityList= myEntityRepository.findAll(1);
    return ResponseEntity.ok(new Resources<>(myEntityList));
} 

2.

@RequestMapping(method = RequestMethod.GET, value = "/myEntities")
public ResponseEntity<?> get() {
    ...TO SOME STUFF
    List<MyEntity> myEntityList= myEntityRepository.findAll();
    List<Resource<MyEntity>> resources = new ArrayList<>();
    myEntityList.forEach(me -> {
        resources.add(new Resource<>(me));
    })
    return ResponseEntity.ok(resources);
} 

选项 1. 和 2. 不要将 _links 添加到结果中,我不明白为什么。我用谷歌搜索了很多,您可以手动添加链接,但这似乎是一种更清晰的方法。谁能明白,我做错了什么?

【问题讨论】:

    标签: spring spring-data spring-data-jpa


    【解决方案1】:

    类似问题的答案在“adding association links to spring data rest custom exposed method”和“Enable HAL serialization in Spring Boot for custom controller method”。为了解决类似的情况,我尝试了一些与这些答案稍有不同的方法,并且效果很好。我希望你能解决你的问题。

    它遵循我如何解决我的具体情况:

        @PostMapping(value = "/myEntities/searchWithParams"
            , consumes = MediaType.APPLICATION_JSON_VALUE
            , produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<?> searchWithParams(@RequestBody SearchParamsDtoClass  params, PersistentEntityResourceAssembler assembler)
    {
        List<MyEntity> entities = myEntityService.searchWithParams(params);
        List<PersistentEntityResource> resourcesList = new ArrayList<PersistentEntityResource>();
    
        for (MyEntity entity: entities) {
            PersistentEntityResource resource = assembler.toResource(entity);
            resourcesList.add(resource);
        }
    
        Resources<PersistentEntityResource> resources = new Resources(resourcesList);
    
        return ResponseEntity.ok(resources);
    }
    

    【讨论】:

    • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
    • 来了,@Liam。
    • 正如我已经完成了审稿人所说的不足,他应该撤消他的反对票。我是这个论坛的新手,不知道有这样的要求才能让评论被广泛接受。这种反馈不能鼓励任何人参与。
    • 假设是我的 -1,但不是。我查看了您的答案,因为其他人将其标记为不是答案。您现在已更正此问题,因此不会被删除。
    • 我很抱歉我的假设。无论如何,审稿人不能撤消反对票吗?
    【解决方案2】:

    Resources 构造函数接受嵌入内容的集合,这与链接的集合不同。您必须手动添加链接。

    Resources resources = new Resources();
    resources.add(myEntityRepository
        .findAll()
        .stream()
        .map(entry -> convertToLink(entry)
        .collect(Collectors.toList()));
    return ResponseEntity.ok(resources);
    

    这是一个示例,其中包含符合 HAL 格式的嵌入内容和分页。

    import static java.util.Objects.*;
    import static java.util.Optional.*;
    import static org.elasticsearch.index.query.QueryBuilders.*;
    import static org.springframework.hateoas.MediaTypes.*;
    import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*;
    import static org.springframework.http.HttpStatus.*;
    import static org.springframework.http.MediaType.*;
    import static org.springframework.web.bind.annotation.RequestMethod.*;
    
    import java.util.ArrayList;
    
    import javax.inject.Inject;
    import javax.validation.constraints.Min;
    import javax.validation.constraints.NotNull;
    
    import org.elasticsearch.index.query.QueryBuilder;
    import org.springframework.data.domain.Page;
    import org.springframework.data.domain.PageRequest;
    import org.springframework.data.domain.Sort;
    import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
    import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
    import org.springframework.hateoas.Link;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.codahale.metrics.annotation.Timed;
    
    @RestController
    @RequestMapping("/catalog/users")
    public class UserResource {
    
        private final @NotNull UserLocator locator;
        private final @NotNull ElasticsearchOperations elasticsearchTemplate;
    
        @Inject
        public UserResource(
            final @NotNull UserLocator locator,
            final @NotNull ElasticsearchOperations elasticsearchTemplate
        ) {
            this.locator = requireNonNull(locator, "locator cannot be null");
            this.elasticsearchTemplate = requireNonNull(elasticsearchTemplate, "elasticsearchTemplate cannot be null");
        }
    
        /**
        * GET  /users : get all the users.
        *
        * @return the ResponseEntity with status 200 (OK) and the list of users in body
        */
        @Timed
        @RequestMapping(method = GET, produces = { HAL_JSON_VALUE, APPLICATION_JSON_VALUE })
        public ResponseEntity<Representations<User>> allUsers(
            @RequestParam(name = "page", required = false, defaultValue =  "0") @Min(0) int page,
            @RequestParam(name = "size", required = false, defaultValue = "25") @Min(1) int size,
            @RequestParam(name = "like", required = false) String like
        ) {
            final PageRequest pageRequest = new PageRequest(page, size, Sort.Direction.ASC, "surname.raw", "givenName.raw");
            final Page<User> entries = elasticsearchTemplate.queryForPage(
                new NativeSearchQueryBuilder()
                    .withQuery(startingWith(like))
                    .withPageable(pageRequest)
                    .build(),
                User.class
            );
    
            final ArrayList<Link> links = new ArrayList<>();
            links.add(linkTo(UserResource.class).withSelfRel());
    
            if (!entries.isFirst()) {
                links.add(linkTo(methodOn(UserResource.class).allUsers(0, size, like)).withRel("first"));
            }
            if (!entries.isLast()) {
                links.add(linkTo(methodOn(UserResource.class).allUsers(entries.getTotalPages() - 1, size, like)).withRel("last"));
            }
            if (entries.hasNext()) {
                links.add(linkTo(methodOn(UserResource.class).allUsers(entries.nextPageable().getPageNumber(), size, like)).withRel("next"));
            }
            if (entries.hasPrevious()) {
                links.add(linkTo(methodOn(UserResource.class).allUsers(entries.previousPageable().getPageNumber(), size, like)).withRel("prev"));
            }
    
            final Representations<User> resourceList = new Representations<>(entries, Representations.extractMetadata(entries), links);
            return ResponseEntity.ok(resourceList);
        }
    
        private QueryBuilder startingWith(String like) {
            return isNull(like) ? null : matchPhrasePrefixQuery("_all", like);
        }
    
        /**
        * GET  /users/:identifier : get the "identifier" user.
        *
        * @param identifier the identifier of the role to retrieve
        * @return the ResponseEntity with status 200 (OK) and with body the role, or with status 404 (Not Found) or with 410 (Gone)
        */
        @Timed
        @RequestMapping(value = "/{identifier}", method = GET, produces = APPLICATION_JSON_VALUE)
        public ResponseEntity<UserRepresentation> aUser(
            @PathVariable("identifier") @NotNull String identifier
        ) {
            return ofNullable(this.locator.findOne(identifier))
                .map(user -> toRepresentation(user))
                .map(ResponseEntity::ok)
                .orElse(notFound())
                ;
        }
    
        private @NotNull UserRepresentation toRepresentation(final @NotNull User role) {
            final String id = role.getIdentifier();
            final Link self = linkTo(methodOn(UserResource.class).aUser(id)).withSelfRel().expand(id);
            final Link collection = linkTo(UserResource.class).withRel("collection");
            return new UserRepresentation(role, self, collection);
        }
    
        protected final @NotNull <U> ResponseEntity<U> notFound() {
            return new ResponseEntity<>(NOT_FOUND);
        }
    
    }
    
    import java.util.Optional;
    
    import org.springframework.hateoas.Link;
    import org.springframework.hateoas.core.Relation;
    
    @Relation(value = "item", collectionRelation = "items")
    public class UserRepresentation extends Representation<User> {
    
        public UserRepresentation(User content, Iterable<Optional<Link>> links) {
            super(content, links);
        }
    
        public UserRepresentation(User content, Link... links) {
            super(content, links);
        }
    
        @SafeVarargs
        public UserRepresentation(User content, Optional<Link>... links) {
            super(content, links);
        }
    
        public UserRepresentation(User content) {
            super(content);
        }
    
    }
    
    import static java.util.Arrays.*;
    import static java.util.Collections.*;
    import static java.util.Objects.*;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Optional;
    
    import javax.validation.constraints.NotNull;
    import javax.validation.constraints.Size;
    
    import org.springframework.hateoas.Link;
    import org.springframework.hateoas.Resource;
    import org.springframework.hateoas.ResourceSupport;
    
    import com.fasterxml.jackson.annotation.JsonInclude;
    import com.fasterxml.jackson.annotation.JsonInclude.Include;
    import com.fasterxml.jackson.annotation.JsonProperty;
    
    public class Representation<T> extends Resource<T> {
        private final Map<String, ResourceSupport> embedded = new HashMap<>();
    
        public Representation(final @NotNull T content) {
            super(content, emptyList());
        }
    
        public Representation(final @NotNull T content, final @NotNull Link... links) {
            super(content, links);
        }
    
        @SafeVarargs
        public Representation(final @NotNull T content, final @NotNull Optional<Link>... links) {
            this(content, (Iterable<Optional<Link>>) asList(links));
        }
    
        public Representation(final @NotNull T content, final @NotNull Iterable<Optional<Link>> links) {
            super(content, emptyList());
            asStream(links).forEach(this::add);
        }
    
        public void add(final @NotNull Optional<Link> link) {
            if (null != link && link.isPresent()) super.add(link.get());
        }
    
        @JsonProperty("_embedded")
        @JsonInclude(Include.NON_EMPTY)
        public final @NotNull Map<String, ResourceSupport> getEmbedded() {
            return this.embedded;
        }
    
        /**
        * @param rel must not be {@literal null} or empty.
        * @param resource the resource to embed
        */
        public final void embed(final @NotNull @Size(min=1) String rel, final ResourceSupport resource) {
            requireNonNull(rel, "rel cannot be null");
            if (rel.trim().isEmpty()) {
                throw new IllegalArgumentException("rel cannot be empty");
            }
            if (null != resource) {
                this.embedded.put(rel, resource);
            }
        }  
    
    }
    
    import javax.validation.constraints.NotNull;
    
    import org.springframework.data.domain.Page;
    import org.springframework.hateoas.Link;
    import org.springframework.hateoas.Resources;
    import org.springframework.hateoas.PagedResources.PageMetadata;
    import org.springframework.hateoas.core.Relation;
    
    import com.fasterxml.jackson.annotation.JsonInclude;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import com.fasterxml.jackson.annotation.JsonUnwrapped;
    
    @Relation(collectionRelation = "items")
    public class Representations<T> extends Resources<T> {
    
        @JsonUnwrapped
        @JsonInclude(JsonInclude.Include.NON_NULL)
        private final PageMetadata metadata;
    
        public Representations(Iterable<T> content) {
            super(content);
            this.metadata = null;
        }
    
        public Representations(Iterable<T> content, PageMetadata metadata) {
            super(content);
            this.metadata = metadata;
        }
    
        public Representations(Iterable<T> content, Iterable<Link> links) {
            super(content, links);
            this.metadata = null;
        }
    
        public Representations(Iterable<T> content, PageMetadata metadata, Iterable<Link> links) {
            super(content, links);
            this.metadata = metadata;
        }
    
        public Representations(Iterable<T> content, Link... links) {
            super(content, links);
            this.metadata = null;
        }
    
        public Representations(Iterable<T> content, PageMetadata metadata, Link... links) {
            super(content, links);
            this.metadata = metadata;
        }
    
        /**
        * Returns the pagination metadata.
        * 
        * @return the metadata
        */
        @JsonProperty("page")
        public PageMetadata getMetadata() {
            return metadata;
        }
    
        public static <U> PageMetadata extractMetadata(final @NotNull Page<U> page) {
            return new PageMetadata(page.getSize(), page.getNumber(), page.getTotalElements(), page.getTotalPages());
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2014-10-06
      • 1970-01-01
      • 2015-04-07
      • 2014-08-25
      • 1970-01-01
      • 1970-01-01
      • 2016-10-15
      • 1970-01-01
      • 2018-08-28
      相关资源
      最近更新 更多