How to remove the "_embedded" property in Spring HATEOAS
Asked Answered
M

7

32

I'm using Spring Boot and HATEOAS to build a REST API and when my API returns a collection, it is wrapped inside a "_embedded" property, like so:

{
   "_links":{
      "self":{
         "href":"http://localhost:8080/technologies"
      }
   },
   "_embedded":{
      "technologies":[
         {
            "id":1,
            "description":"A",
            "_links":{
               "self":{
                  "href":"http://localhost:8080/technologies/1"
               }
            }
         },
         {
            "id":2,
            "description":"B",
            "_links":{
               "self":{
                  "href":"http://localhost:8080/technologies/2"
               }
            }
         }
      ]
   }
}

I want the response to be like this:

{
   "_links":{
      "self":{
         "href":"http://localhost:8080/technologies"
      }
   },
   "technologies":[
      {
         "id":1,
         "description":"A",
         "_links":{
            "self":{
               "href":"http://localhost:8080/technologies/1"
            }
         }
      },
      {
         "id":2,
         "description":"B",
         "_links":{
            "self":{
               "href":"http://localhost:8080/technologies/2"
            }
         }
      }
   ]
}

My TechnologiesController:

@RestController
@ExposesResourceFor(Technology.class)
@RequestMapping(value = "/technologies")
public class TechnologiesController {
    ...
    @ResquestMapping(method = RequestMethod.GET, produces = "application/vnd.xpto-technologies.text+json")
    public Resources<Resource<Technology>> getAllTechnologies() {
        List<Technology> technologies = technologyGateway.getAllTechnologies();
        Resources<<Resource<Technology>> resources = new Resources<Resource<Technology>>(technologyResourceAssembler.toResources(technologies));
        resources.add(linkTo(methodOn(TechnologiesController.class).getAllTechnologies()).withSelfRel());
        return resources;
    }

The configuration class has the annotation @EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL).

What is the best way to produce the response without the "_embedded"?

Mailemailed answered 2/3, 2015 at 11:4 Comment(4)
If you remove _embedded from the response then the response will no longer be valid HAL. You either need to stick with _embedded or use a different media type.Kephart
The HAL draft says "The reserved "_embedded" property is OPTIONAL"Mailemailed
It's optional in that a resource doesn't have to have any embedded resources. However, if it does, then they should be beneath _embedded.Kephart
I'm having this same issue. I have a projection on an object, and am limiting it to only display the name. The problem is that there are over 20 relations, so the _embedded object is huge. I haven't found a good way to overcome this either.Hovercraft
G
10

I close HAL feature, because it is hard to using Resources/Resource by restTemplate. I disable this feature by following code:

public class SpringRestConfiguration implements RepositoryRestConfigurer {
    @Override
    public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {

        config.setDefaultMediaType(MediaType.APPLICATION_JSON);
        config.useHalAsDefaultJsonMediaType(false);
    }
}

It work for me. HAL is good if there are more support with restTemplate.

Grubbs answered 9/11, 2018 at 9:15 Comment(1)
it works but actually doesnt solve the problem at all. Instead of returning the data inside _embedded field it creates a content field which is better but doesnt create the technologies field required by the author;Satinwood
I
9

As the documentation says

application/hal+json responses should be sent to requests that accept application/json

In order to omit _embedded in you response you'll need to add

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

to application.properties.

Incense answered 26/8, 2016 at 19:56 Comment(1)
I did it. but, unfortunately, it still existsChiu
P
6

Adding this Accept header to the request:

Accept : application/x-spring-data-verbose+json
Prettify answered 10/12, 2015 at 18:0 Comment(1)
For me this returns a 406 with the message: "Could not find acceptable representation"Marchioness
R
3

For those who use Spring Data, and consider it as a problem - solution is to set

spring.data.rest.defaultMediaType = application/json

in application properties. There still links will be available, but no _embedded any more.

Rathe answered 11/9, 2018 at 22:29 Comment(2)
This did not change anything for me.Franciscafranciscan
This removed _embedded from the request however, added "content" instead. the true rest interface should return "technologies" instead. no ?Fullfaced
F
3

What you're describing in the produced and expected results are semantically different things. The former thing is the HAL representation of a Collection<Technology>. The latter, which you expect is the representation of:

class Wrapper {
  Resources<Technology> technologies;
}

Note how this is how we actually create the top level technologies property that you would like to see in your response. You don't create any of the latter in your controller. A top-level Resourcesinstance is basically a collection and the only way to represent a top-level collection in HAL is _embedded. Apparently you don't want that but that's what you have written in your controller method.

Assuming you have Wrapper, something like this should work (untested):

Wrapper wrapper = new Wrapper(assembler.toCollectionModel(technologies);
EntityModel<Wrapper> model = EntityModel.of(wrapper);
model.add(linkTo(…));

PS: As of Spring HATEOAS 1.0, Resources is CollectionModel and Resourceis EntityModel.

Fairing answered 9/3, 2020 at 22:23 Comment(1)
This answer, though untested, goes beyond a configuration change that doesn't address the problem. Thank you.Gileadite
B
0

You can use this code in the service

  constructor(
    private httpClient: HttpClient
  ) { }

  retrieveAllStudents(){
    return this.httpClient.get<any[]>(`http://localhost:8080/students`);
  }

This will deal with the _embedded part of Json and extract the desired data.

export class ListStudentsComponent implements OnInit {

 // declaring variables to be used
  student: Student;
  students: Student[];
  message: string;

  // injecting student service into the constuctor
   constructor(
    private studentService: StudentService,
  ) { }

  ngOnInit() {
    this.refreshStudents();
  }
refreshStudents(){
  this.studentService.retrieveAllStudents().subscribe(
     response => {
       console.log(response);
      this.students = response._embedded.students as Student[];
     }
   );
 }
Bernt answered 4/4, 2019 at 23:50 Comment(0)
F
0

For latest versions in Spring RepositoryRestConfigurer doesn't include the method public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) you'd need to override the default method on RepositoryRestConfigurer which include cors parameter.

public class RestConfiguration implements RepositoryRestConfigurer {

    public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config, CorsRegistry cors) {
        config.setDefaultMediaType(MediaType.APPLICATION_JSON);
        config.useHalAsDefaultJsonMediaType(false);
    }
}

Fanny answered 15/2, 2023 at 11:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.