Java 使用spring-boot-2.2.1移除嵌入HATEOAS链接中的

Java 使用spring-boot-2.2.1移除嵌入HATEOAS链接中的,java,spring,spring-boot,spring-data-mongodb,spring-hateoas,Java,Spring,Spring Boot,Spring Data Mongodb,Spring Hateoas,我正在使用spring-boot-2.2.1和spring-HATEOAS。超媒体链接工作正常,但是 我在返回链接时看到\u embedded属性,请在github中查找以下代码以供参考和项目 端点: a) 将返回CollectionModel=>localhost:8099/api/v1/capability/list/noembedded 及 b) 将返回Listlocalhost:8099/api/v1/Capability/List/ <?xml version="1.0" enc

我正在使用spring-boot-2.2.1和spring-HATEOAS。超媒体链接工作正常,但是 我在返回链接时看到
\u embedded
属性,请在github中查找以下代码以供参考和项目

端点:

a) 将返回
CollectionModel
=>localhost:8099/api/v1/capability/list/noembedded

b) 将返回List>localhost:8099/api/v1/Capability/List/

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>spring-boot-unittest</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-boot-unittest</name>
    <description>Demo project for Spring Boot unit test</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-hateoas</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-mongodb</artifactId>
        </dependency>
        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-mongodb</artifactId>
            <version>4.1.4</version>
        </dependency>

        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-apt</artifactId>
            <version>4.1.4</version>
        </dependency>
        <!-- Embedded  MongoDB for Testing -->
        <dependency>
            <groupId>de.flapdoodle.embed</groupId>
            <artifactId>de.flapdoodle.embed.mongo</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-ui</artifactId>
            <version>1.2.21</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>com.mysema.maven</groupId>
                <artifactId>apt-maven-plugin</artifactId>
                <version>1.1.3</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>process</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>target/generated-sources/java</outputDirectory>
                            <!--<processor>com.querydsl.mongodb.morphia.MorphiaAnnotationProcessor</processor>-->
                            <processor>org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor</processor>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>
预期答复:

[
  {
    "id": "sample",
    "techStack": "Java",
    "numOfDevelopers": 25,
    "numOfAvailableDevelopers": 10,
    "_links": {
      "getThisCapability": {
        "href": "http://localhost:8099/api/v1/capability/sample"
      },
      "getAllCapabilities": {
        "href": "http://localhost:8099/api/v1/capability/list"
      }
    }
  }
]
我试过了

spring.data.rest.defaultMediaType=application/json

使用hal作为默认json媒体类型=false

但是运气不好,我仍然能够在响应中看到
\u嵌入的
属性。
有谁能帮我确定这个问题吗

在此之前,我使用的是
spring-boot-1.5.10
,我能够在不嵌入
\u的情况下正确呈现链接,请参阅

在我在主类中添加了下面的注释之后,如果我返回列表,它就可以正常工作了

@EnableHypermediaSupport(type=EnableHypermediaSupport.HypermediaType.HAL)

localhost:9771/api/v1/capability/list

这将产生以下结果:

[
  {
    "capabilityId": "sample",
    "techStack": "Java",
    "numOfDevelopers": 25,
    "numOfAvailableDevelopers": 10,
    "_links": {
      "getThisCapability": {
        "href": "http://localhost:9771/api/v1/capability/sample"
      },
      "getAllCapabilities": {
        "href": "http://localhost:9771/api/v1/capability/list"
      }
    }
  }
]
不幸的是,它在最新版本中不起作用。 任何帮助都将不胜感激。

如果我理解正确,这将是无效的HAL,这就是为什么Spring HATEOAS不会产生此结果,只要您在请求中返回
CollectionModel
。请注意,集合也可能有链接,链接将位于
\u links
属性中嵌入的
\u属性旁边,如中所示

如果您真的想去掉
\u embedded
属性并生成一个
EntityModel
列表,那么如果您修改代码以返回一个
列表,它应该可以工作。然后,您将丢失Spring HATEOAS生成的
\u嵌入式
\u链接
属性。以下是修改后的代码段:

@RestController
@RequestMapping(value = "/api/v1/capability")
@RequiredArgsConstructor
@CrossOrigin
public class CapabilityController {

    private final CapabilityService capabilityService;
    private final CapabilityResourceAssembler capabilityResourceAssembler;

    @GetMapping(value = "/list")
    public List<EntityModel<Capability>> getAllCapabilities() {
       List<EntityModel<Capability>> capabilities = capabilityService.listCapabilities().stream()
               .map(capability -> new EntityModel<>(capability,
                       linkTo(methodOn(CapabilityController.class).getCapabilityById(capability.getId())).withRel("getThisCapability"),
                       linkTo(methodOn(CapabilityController.class).getAllCapabilities()).withRel("getAllCapabilities")
               )).collect(Collectors.toList());
        return capabilities;
    }
}
@RestController
@请求映射(value=“/api/v1/capability”)
@所需参数构造函数
@交叉起源
公共类能力控制器{
私人最终能力服务能力服务;
私人最终能力资源装配商能力资源装配商;
@GetMapping(value=“/list”)
公共列表getAllCapabilities(){
列表功能=capabilityService.listCapabilities().stream()
.map(能力->新实体模型(能力,
链接到(methodOn(CapabilityController.class).getCapabilityById(capability.getId()).withRel(“getThisCapability”),
链接到(methodOn(CapabilityController.class).getAllCapabilities()).withRel(“getAllCapabilities”)
)).collect(Collectors.toList());
返回能力;
}
}
我强烈建议你不要这样做,因为你失去了春夏的好处

此外,我建议您使用
capabilityresourcesembler
,并通过使用
capabilityresourcesembler.toModel(…)
创建
EntityModel
实例,这样您就不必重复在
.map(…)中实现的代码
函数。

如果我理解正确,这将是无效的HAL,这就是为什么只要在请求中返回
CollectionModel
,Spring HATEOAS就不会产生此结果。请注意,集合也可能有链接,链接将位于
\u links
属性中嵌入的
\u属性旁边,如中所示

如果您真的想去掉
\u embedded
属性并生成一个
EntityModel
列表,那么如果您修改代码以返回一个
列表,它应该可以工作。然后,您将丢失Spring HATEOAS生成的
\u嵌入式
\u链接
属性。以下是修改后的代码段:

@RestController
@RequestMapping(value = "/api/v1/capability")
@RequiredArgsConstructor
@CrossOrigin
public class CapabilityController {

    private final CapabilityService capabilityService;
    private final CapabilityResourceAssembler capabilityResourceAssembler;

    @GetMapping(value = "/list")
    public List<EntityModel<Capability>> getAllCapabilities() {
       List<EntityModel<Capability>> capabilities = capabilityService.listCapabilities().stream()
               .map(capability -> new EntityModel<>(capability,
                       linkTo(methodOn(CapabilityController.class).getCapabilityById(capability.getId())).withRel("getThisCapability"),
                       linkTo(methodOn(CapabilityController.class).getAllCapabilities()).withRel("getAllCapabilities")
               )).collect(Collectors.toList());
        return capabilities;
    }
}
@RestController
@请求映射(value=“/api/v1/capability”)
@所需参数构造函数
@交叉起源
公共类能力控制器{
私人最终能力服务能力服务;
私人最终能力资源装配商能力资源装配商;
@GetMapping(value=“/list”)
公共列表getAllCapabilities(){
列表功能=capabilityService.listCapabilities().stream()
.map(能力->新实体模型(能力,
链接到(methodOn(CapabilityController.class).getCapabilityById(capability.getId()).withRel(“getThisCapability”),
链接到(methodOn(CapabilityController.class).getAllCapabilities()).withRel(“getAllCapabilities”)
)).collect(Collectors.toList());
返回能力;
}
}
我强烈建议你不要这样做,因为你失去了春夏的好处


此外,我建议您使用
功能资源Sembler
,通过使用
功能资源Sembler.toModel(…)
创建
实体模型
实例,这样您就不必重复在
.map(…)
函数中实现的代码。

使用Spring Boot 1.5时,您依赖于Spring HATEOAS的限制,这导致Jackson
ObjectMapper
被用作应用程序范围的
ObjectMapper
,该对象映射器是特定于HAL的。这种污染意味着HAL格式在不应该应用的情况下应用于响应

Spring HATEOAS 1.0解决了这一限制,其特定于HAL的
ObjectMapper
不再污染整个应用程序。如果您希望主应用程序
ObjectMapper
应用HAL样式的序列化,您可以通过自定义它来选择返回:

@Bean
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder,
                                 HypermediaMappingInformation mappingInformation) {
    ObjectMapper objectMapper = builder.build();
    mappingInformation.configureObjectMapper(objectMapper);
    return objectMapper;
}
虽然我认为上述方法会奏效,但我将回应他们在答复中提出的对答复格式和内容的关注
@Bean
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder,
                                 HypermediaMappingInformation mappingInformation) {
    ObjectMapper objectMapper = builder.build();
    mappingInformation.configureObjectMapper(objectMapper);
    return objectMapper;
}
public class CustomCollectionModel<T> extends CollectionModel<CollectionModel<T>>  {
@JsonValue
private Collection<T> content;

protected CustomCollectionModel() {
    this(new ArrayList<>());
}

public CustomCollectionModel(Iterable<T> content, Link... links) {
    this(content, Arrays.asList(links));
}


public CustomCollectionModel(Iterable<T> content, Iterable<Link> links) {

    Assert.notNull(content, "Content must not be null!");

    this.content = new ArrayList<>();

    for (T element : content) {
        this.content.add(element);
    }

    this.add(links);
}}
  @Override
public CustomCollectionModel toCollectionModel(Iterable<? extends T> entities) {
    List<EntityModel<T>> resourceList = new ArrayList<>();

    for (T entity : entities) {
        resourceList.add(toModel(entity));
    }
    return new CustomCollectionModel(resourceList);
}