Search code examples
springrestspring-boothateoasspring-hateoas

Spring Boot HATEOAS Output Fails


The output of my entities in a Sprint Boot REST HATEOAS service does not work. The service returns an empty string for each entity. There are no error messages. I have tried Spring Boot 1.5.4 and 2.0.0.RC1.

Full source code is on GitHub: https://github.com/murygin/hateoas-people-service

Application

@SpringBootApplication
@Configuration
@EnableHypermediaSupport(type={EnableHypermediaSupport.HypermediaType.HAL})
public class Application {   
  public static void main(String args[]) {
    SpringApplication.run(Application.class);
  }
}

PersonController

@RestController
@RequestMapping(value = "/persons", produces = "application/hal+json")
public class PersonController {

  @GetMapping
  public ResponseEntity<Resources<PersonResource>> all() {
    final List<PersonResource> collection =
        getPersonList().stream().map(PersonResource::new).collect(Collectors.toList());
    final Resources<PersonResource> resources = new Resources<>(collection);
    final String uriString = ServletUriComponentsBuilder.fromCurrentRequest().build().toUriString();
    resources.add(new Link(uriString, "self"));
    return ResponseEntity.ok(resources);
  }

  @GetMapping("/{id}")
  public ResponseEntity<PersonResource> get(@PathVariable final long id) {
    Person p = new Person((long)1,"Donald","Duck");
    return ResponseEntity.ok(new PersonResource(p));
  }

  private List<Person> getPersonList() {
    List<Person> personList = new LinkedList<>();
    personList.add(new Person((long)1,"Donald","Duck"));
    personList.add(new Person((long)2,"Dagobert","Duck"));
    personList.add(new Person((long)3,"Daniel","Duesentrieb"));
    return personList;
  }

}

PersonResource

public class PersonResource extends ResourceSupport {

  private final Person person;

  public PersonResource(final Person person) {
    this.person = person;
    final long id = person.getId();
    add(linkTo(PersonController.class).withRel("people"));
    add(linkTo(methodOn(PersonController.class).get(id)).withSelfRel());
  }
}

Person

public class Person {    
  private Long id;   
  private String firstName;   
  private String secondName;

  public Person() {
  }

  public Person(Long id, String firstName, String secondName) {
    this.id = id;
    this.firstName = firstName;
    this.secondName = secondName;
  }

  // getter and setter...
}

Output of http://localhost:8080/persons

    {
        _embedded: {
            personResourceList: [{
                    _links: {
                        people: {
                            href: "http://localhost:8080/persons"
                        },
                        self: {
                            href: "http://localhost:8080/persons/1"
                        }
                    }
                },
                {
                    _links: {
                        people: {
                            href: "http://localhost:8080/persons"
                        },
                        self: {
                            href: "http://localhost:8080/persons/2"
                        }
                    }
                },
                {
                    _links: {
                        people: {
                            href: "http://localhost:8080/persons"
                        },
                        self: {
                            href: "http://localhost:8080/persons/3"
                        }
                    }
                }
            ]
        },
        _links: {
            self: {
                href: "http://localhost:8080/persons"
            }
        }
    }

Output of http://localhost:8080/persons/1

    {
        _links: {
            people: {
                href: "http://localhost:8080/persons"
            },
            self: {
                href: "http://localhost:8080/persons/1"
            }
        }
    }

Solution

  • Add a getter for person in PersonResource:

    public class PersonResource extends ResourceSupport {
    
      private final Person person;
    
      public PersonResource(final Person person) {
        this.person = person;
        final long id = person.getId();
        add(linkTo(PersonController.class).withRel("people"));
        add(linkTo(methodOn(PersonController.class).get(id)).withSelfRel());
      }
    
        public Person getPerson() {
            return person;
        }
    }
    

    With the getter, Spring gets the person wrapped in your PersonResource and serializes it:

    GET http://localhost:8080/persons/1

    {
      "person" : {
        "id" : 1,
        "firstName" : "Donald",
        "secondName" : "Duck"
      },
      "_links" : {
        "people" : {
          "href" : "http://localhost:8080/persons"
        },
        "self" : {
          "href" : "http://localhost:8080/persons/1"
        }
      }
    }
    

    GET http://localhost:8080/persons

    {
      "_embedded" : {
        "personResources" : [ {
          "person" : {
            "id" : 1,
            "firstName" : "Donald",
            "secondName" : "Duck"
          },
          "_links" : {
            "people" : {
              "href" : "http://localhost:8080/persons"
            },
            "self" : {
              "href" : "http://localhost:8080/persons/1"
            }
          }
        }, {
          "person" : {
            "id" : 2,
            "firstName" : "Dagobert",
            "secondName" : "Duck"
          },
          "_links" : {
            "people" : {
              "href" : "http://localhost:8080/persons"
            },
            "self" : {
              "href" : "http://localhost:8080/persons/2"
            }
          }
        }, {
          "person" : {
            "id" : 3,
            "firstName" : "Daniel",
            "secondName" : "Duesentrieb"
          },
          "_links" : {
            "people" : {
              "href" : "http://localhost:8080/persons"
            },
            "self" : {
              "href" : "http://localhost:8080/persons/3"
            }
          }
        } ]
      },
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/persons"
        }
      }
    }
    

    Note: I'm a lazy bum, I added spring-boot-starter-data-rest to the pom.xml dependencies to pretty print the result, so your actual result may vary a bit.