Xeperis
Xeperis

Reputation: 1459

Spring Boot - RESTful controllers, hateoas and JSON

So I went through some documentation and examples on using spring hateoas (with spring-boot). And so by following examples I have created 2 Controllers.

Here is a snippet of the first one:

@RestController
@RequestMapping(value = "/users", produces = MediaType.APPLICATION_JSON_VALUE)
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping(method = RequestMethod.GET)
    public HttpEntity<Resources<Resource<UserResponse>>> findAll() {
        List<UserResponse> userList = userService.finaAll();

        List<Resource<UserResponse>> resources = Lists.newArrayList();
        for (UserResponse user : userList) {
            Resource<UserResponse> userResource = new Resource<UserResponse>(user);
            resources.add(userResource);
            userResource.add(linkTo(methodOn(UserController.class).findAll()).withSelfRel());
            userResource.add(linkTo(methodOn(UserController.class).findById(user.getId())).withRel("viewUser"));
        }

        return new ResponseEntity(new Resources(resources), HttpStatus.OK);
    }
}

And here is the UserResponse DTO:

public class UserResponse {

    private Long id;

    private String firstName;

    private String lastName;

    private String socialNumber;

    @JsonDeserialize(using = LocalDateDeserializer.class)
    @JsonSerialize(using = LocalDateSerializer.class)
    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
    private LocalDate dateOfBirth;
}

Getters and setters are removed for brevity the only thing that might be worth mentioning is that there is @JsonIgnore and @JsonProperty on getter/setter of ID attribute.

Now the response I get is following:

{
  "_embedded" : {
    "userResponses" : [ {
      "firstName" : "Brand",
      "lastName" : "White",
      "socialNumber" : "342asd3423",
      "dateOfBirth" : "1987-04-04",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/users"
        },
        "viewUser" : {
          "href" : "http://localhost:8080/users/10"
        }
      }
    }, {
      "firstName" : "Snow",
      "lastName" : "Green",
      "socialNumber" : "3423cxvx423",
      "dateOfBirth" : "1987-01-12",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/users"
        },
        "viewUser" : {
          "href" : "http://localhost:8080/users/11"
        }
      }
    } ]
  }
}

Question One: according to http://docs.spring.io/spring-hateoas/docs/current/reference/html/ the format should be somethign along the lines:

links : [ { rel : "self", href : "http://myhost/people" } ] } in my example it looks like like "rel" is an attribute.

I have a second controller which was basically copy pasted:

@RestController
@RequestMapping(value = "/users/{userId}/details", produces = MediaType.APPLICATION_JSON_VALUE)
public class DetailsController {
    //... services ...
    @RequestMapping(method = RequestMethod.GET)
    public HttpEntity<Resources<Resource<DetailsResponse>>> findAllUserDetails(@PathVariable("userId") Long userId) {
        Iterable<UserDetails> details = userDetails.findAll();

        List<Resource<DetailsResponse>> hyperList = Lists.newArrayList();

        for (UserDetails detail : details) {
            Resource<DetailsResponse> hyperRes = new Resource<DetailsResponse>(new DetailsResponse(details));
            hyperRes.add(linkTo(DetailsController.class, userId).withSelfRel());
            hyperRes.add(linkTo(DetailsController.class, userId).slash("/" + detail.getId()).withRel("viewDetail"));
            hyperRes.add(linkTo(DetailsController.class, userId).slash("/" + detail.getId()).withRel("updateDetail"));

            hyperList.add(hyperRes);
        }

        return new ResponseEntity(hyperList, HttpStatus.OK);
    }
}

And this single method produces response like this:

{
    "lifespanDays": 60,
    "status": "deceased",
    "dateBorn": [
        2015,
        4,
        5
    ],
    "dateDied": [
        2015,
        6,
        4
    ],
    "links": [
        {
        "rel": "self",
        "href": "http://localhost:8080/users/10/details"
        },
        {
        "rel": "viewDetail",
        "href": "http://localhost:8080/users/10/details/30"
        },
        {
        "rel": "updateDetail",
        "href": "http://localhost:8080/users/10/details/30"
        }
    ]
}

DTO for second controller:

public class UserDetailResponse {

    private Long lifespanDays;

    private String status;

    @JsonDeserialize(using = LocalDateDeserializer.class)
    @JsonSerialize(using = LocalDateSerializer.class)
    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
    private LocalDate dateBorn;

    @JsonDeserialize(using = LocalDateDeserializer.class)
    @JsonSerialize(using = LocalDateSerializer.class)
    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
    private LocalDate dateDied;
}

I do not get it which format is correct for JSON ? And I also do not understand why they differ despite that the produces MediaType specified on the type is application/json for both. Plus the date format in the UserDetail response has also changed....

Both controllers are under same package both are equally annotated. This is all out of the box Spring-Boot with bunch of stater dependencies:

compile('org.springframework.boot:spring-boot-starter-web:1.2.2.RELEASE')
compile('org.springframework.hateoas:spring-hateoas:0.17.0.RELEASE')
compile('org.springframework.boot:spring-boot-starter-data-rest:1.2.2.RELEASE') { transitive = true; }
compile('com.google.code.gson:gson:2.3.1');
compile('org.springframework.boot:spring-boot-starter-data-jpa:1.2.2.RELEASE') { transitive = true; };
compile('com.google.guava:guava:18.0')
compile('commons-beanutils:commons-beanutils:1.9.2')
runtime('org.hsqldb:hsqldb:2.3.2');

Upvotes: 3

Views: 4274

Answers (1)

a better oliver
a better oliver

Reputation: 26828

Question One:

The default format for responses in Spring Boot is HAL, which is what you got. Links are indeed serialized differently than you might expect. Spring HATEOAS registers a module for Jackson that is responsible for that. Btw: the documentation makes no explicit statements about the rendering of links.

Question Two:

Answered by the OP in the comments.

FWIW: Both responses are correct JSON.

Upvotes: 2

Related Questions