spring-projects / spring-boot

Spring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss.
https://spring.io/projects/spring-boot
Apache License 2.0
75.18k stars 40.68k forks source link

Remove _embedded in HATEOAS link using spring-boot-2.2.1 #19939

Closed VelDeveloper closed 4 years ago

VelDeveloper commented 4 years ago

I am using spring-boot-2.2.1 along with spring-HATEOAS. Hypermedia links are working fine but I see _embedded attribute while returning links, Please find the below code for reference,


<?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>
        <dependency>
            <groupId>org.atteo</groupId>
            <artifactId>evo-inflector</artifactId>
            <version>1.2.2</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>

Controller.Java

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

    private final CapabilityService capabilityService;
    private final CapabilityResourceAssembler capabilityResourceAssembler;

    @GetMapping(value = "/list")
    public CollectionModel<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 new CollectionModel<>(capabilities);
    }
}

Actual Response

{
  "_embedded": {
    "capabilityList": [
      {
        "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"
          },
          "deleteThisCapability": {
            "href": "http://localhost:8099/api/v1/capability/sample"
          },
          "createCapability": {
            "href": "localhost:8099/api/v1/capability"
          }
        }
      }
    ]
  }
}

Expected Response

[
  {
    "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"
      }
    }
  }
]

I tried

spring.data.rest.defaultMediaType = application/json

spring.hateoas.use-hal-as-default-json-media-type=false

But no luck still I am able to see _embedded attribute in the response. Could anyone please help me to identify the issue.

I tried to return List<EntityModel<Capability>> here I couldn't see _embedded but _links are renamed to links and the links are not rendered properly. In the older versions, it was absolutely working fine with List<Resource>

Sample response for List<EntityModel<Capability>>

[
  {
    "id": "sample",
    "techStack": "Java",
    "numOfDevelopers": 25,
    "numOfAvailableDevelopers": 10,
    "links": [
      {
        "rel": "getThisCapability",
        "href": "http://localhost:8099/api/v1/capability/sample"
      },
      {
        "rel": "getAllCapabilities",
        "href": "http://localhost:8099/api/v1/capability/list"
      }
    ]
  }
]

Please find the stackoverflow question here

I also tagged spring-boot but no reply so for. Any help would be really appreciated.

wilkinsona commented 4 years ago

I see someone is already trying to help you on Stack Overflow. To avoid duplicating effort, it’s better to keep the discussion in one place so I am going to close this issue for now. We can re-open it if it turns out that a change in Spring Boot is required.

You’ve said above that “In the older versions, it was absolutely working fine with List“. Perhaps you can update your Stack Overflow question with a minimal, complete, and verifiable example of this? It would be useful to know exactly what you did previously and what the old version was.

VelDeveloper commented 4 years ago

@wilkinsona I didn't get an answer in stack-overflow as well. That's the reason I posted the question in the official forum. Could you please help me to resolve the issue instead of forwarding to StackOverflow or could you please answer it in StackOverflow. Maybe a hint would be really helpful.

snicoll commented 4 years ago

That's the reason I posted the question in the official forum.

The issue template that was available when you created this issue mentions that we prefer not to use this issue tracker for questions. This is also mentioned in the guidelines for contributing. Please review those before contributing again.

Maybe a hint would be really helpful.

I believe Andy gave you one already. If you provide the sample he requested on your SO question, you'll increase your chances to get an answer quickly.

VelDeveloper commented 4 years ago

@wilkinsona @snicoll I have updated the question in StackOverflow as per @wilkinsona request. Could you please help with it? In the older version(1.5.10) of spring-boot, it is working fine after I add @EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL) but in the new version of spring-boot(2.2.1), it stopped working. Any help would be really appreciated.

yerzhant commented 4 years ago

On SB 2.2.0.RELEASE in responses there were _embedded fields, but after upgrading to 2.2.5.RELEASE it has disappeared. After sequential downgrading found out that in 2.2.1.RELEASE the field has disappeared.

My frontend is relying on values on the fields inside the _embedded field.

wilkinsona commented 4 years ago

@yerzhant You have the opposite problem to the one reported here and on Stack Overflow. If you believe that you have found a bug, please open a new issue and provide a minimal sample that reproduces the problem either as a zip attached to this issue or pushed to a separate repository.