Spring-boot + REST + HATEOAS + HAL

spring boot wiki
spring boot version
spring boot tutorialspoint
spring boot-starter
spring boot project
spring-boot-maven
spring boot interview questions
spring boot exercises

I followed the spring.io Pivotal tutorials to get a REST API with a MySQL DB off the ground and things are progressing nicely. However I found a behavior that I haven't been able to configure or work around.

When I use the built-in functionality to retrieve my Resources from the PagingAndSortingRepository, the resulting REST is automatically paged and encapsulated with useful HAL links (_links, self, search, linked Resources, etc). I want to use that.

When I implemented my Controller to customize the PostMapping behavior and introduce sanity checks, validation and etc, the GetMapping stopped working. So I re-implemented a GetMapping that leveraged my Service layer.

Doing so unfortunately broke the HATEOAS that was previously provided.

What I would like is to be able to customize the PostMapping, but retain the GetMapping exactly like the default. If possible I would love to avoid having to write it myself since I know the framework can supply it.

Any way to do that?

Controller:

@RestController
public class PartyMemberController {

    @Autowired
    PartyMemberService partyMemberService;

    @RequestMapping(method = RequestMethod.GET, value = "/partyMembers")
    public ResponseEntity<Iterable<PartyMember>> getAllPartyMembers() {
        Iterable<PartyMember> partyMemberList = partyMemberService.getAll();
        return new ResponseEntity<>(partyMemberList, HttpStatus.OK);
    }

    @RequestMapping(method = RequestMethod.POST, value = "/partyMembers")
    public ResponseEntity<PartyMember> addEmployee(@Valid @RequestBody PartyMember partyMember) {
        if (partyMemberService.exists(partyMember)) {
            return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
        }

        partyMember = partyMemberService.save(partyMember);
        return new ResponseEntity<PartyMember>(partyMember, HttpStatus.CREATED);
    }
}

Resulting JSON

    [
      {
        "id": 2,
        "ward": {
          "id": 1,
          "name": "Mercier",
          "wardNumber": 42,
          "numberOfMembers": 3
        },
        "firstName": "Cindy",
        "lastName": "Tremblay",
        "partyMemberId": "12-1234-09876",
        "primaryPhone": "514-555-2323",
        "postalAddress": "1155 Robert-Bourassa, Montreal, Quebec, Canada, H3B3A7",
        "emailAddress": null,
        "secondaryPhone": null,
        "bestTimeToContact": null,
        "bestWayToContact": null,
        "membershipExpiry": null,
        "dateOfBirth": null,
        "donationEntries": [],
        "note": null
      },
      {
        "id": 3,
        "ward": {
          "id": 1,
          "name": "Mercier",
          "wardNumber": 42,
          "numberOfMembers": 3
        },
        "firstName": "Robert",
        "lastName": "Paulson",
        "partyMemberId": "12-1234-54321",
        "primaryPhone": "514-555-1212",
        "postalAddress": "440 Rue Saint-Pierre, App 5, Montreal, Quebec, Canada, H2Y2M5",
        "emailAddress": "rpaulson@papermillsoapcompany.com",
        "secondaryPhone": null,
        "bestTimeToContact": null,
        "bestWayToContact": null,
        "membershipExpiry": null,
        "dateOfBirth": null,
        "donationEntries": [],
        "note": null
      },
      {
        "id": 4,
        "ward": {
          "id": 1,
          "name": "Mercier",
          "wardNumber": 42,
          "numberOfMembers": 3
        },
        "firstName": "Richard",
        "lastName": "Schnobb",
        "partyMemberId": "12-4321-09876",
        "primaryPhone": "514-555-2323",
        "postalAddress": "440 Rue Saint-Pierre, App 5, Montreal, Quebec, Canada, H2Y2M5",
        "emailAddress": null,
        "secondaryPhone": null,
        "bestTimeToContact": null,
        "bestWayToContact": null,
        "membershipExpiry": null,
        "dateOfBirth": null,
        "donationEntries": [],
        "note": null
      }
    ]

Default JSON (Notice the _embedded, _links, etc). This what I want to have as a result.

{
  "_embedded" : {
    "partyMembers" : [ {
      "firstName" : "Cindy",
      "lastName" : "Tremblay",
      "partyMemberId" : "12-1234-09876",
      "primaryPhone" : "514-555-2323",
      "postalAddress" : "1155 Robert-Bourassa, Montreal, Quebec, Canada, H3B3A7",
      "emailAddress" : null,
      "secondaryPhone" : null,
      "bestTimeToContact" : null,
      "bestWayToContact" : null,
      "membershipExpiry" : null,
      "dateOfBirth" : null,
      "donationEntries" : [ ],
      "note" : null,
      "_links" : {
        "self" : {
          "href" : "http://127.0.0.1:8080/partyMembers/2"
        },
        "partyMember" : {
          "href" : "http://127.0.0.1:8080/partyMembers/2"
        },
        "ward" : {
          "href" : "http://127.0.0.1:8080/partyMembers/2/ward"
        }
      }
    }, {
      "firstName" : "Robert",
      "lastName" : "Paulson",
      "partyMemberId" : "12-1234-54321",
      "primaryPhone" : "514-555-1212",
      "postalAddress" : "440 Rue Saint-Pierre, App 5, Montreal, Quebec, Canada, H2Y2M5",
      "emailAddress" : "rpaulson@papermillsoapcompany.com",
      "secondaryPhone" : null,
      "bestTimeToContact" : null,
      "bestWayToContact" : null,
      "membershipExpiry" : null,
      "dateOfBirth" : null,
      "donationEntries" : [ ],
      "note" : null,
      "_links" : {
        "self" : {
          "href" : "http://127.0.0.1:8080/partyMembers/3"
        },
        "partyMember" : {
          "href" : "http://127.0.0.1:8080/partyMembers/3"
        },
        "ward" : {
          "href" : "http://127.0.0.1:8080/partyMembers/3/ward"
        }
      }
    }, {
      "firstName" : "Richard",
      "lastName" : "Schnobb",
      "partyMemberId" : "12-4321-09876",
      "primaryPhone" : "514-555-2323",
      "postalAddress" : "440 Rue Saint-Pierre, App 5, Montreal, Quebec, Canada, H2Y2M5",
      "emailAddress" : null,
      "secondaryPhone" : null,
      "bestTimeToContact" : null,
      "bestWayToContact" : null,
      "membershipExpiry" : null,
      "dateOfBirth" : null,
      "donationEntries" : [ ],
      "note" : null,
      "_links" : {
        "self" : {
          "href" : "http://127.0.0.1:8080/partyMembers/4"
        },
        "partyMember" : {
          "href" : "http://127.0.0.1:8080/partyMembers/4"
        },
        "ward" : {
          "href" : "http://127.0.0.1:8080/partyMembers/4/ward"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://127.0.0.1:8080/partyMembers{?page,size,sort}",
      "templated" : true
    },
    "profile" : {
      "href" : "http://127.0.0.1:8080/profile/partyMembers"
    },
    "search" : {
      "href" : "http://127.0.0.1:8080/partyMembers/search"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 3,
    "totalPages" : 1,
    "number" : 0
  }
}

To keep Spring doing most of the work for you, you should use the annotation @RepositoryRestController. More explanations in the docs:

Sometimes, you may want to write a custom handler for a specific resource. To take advantage of Spring Data REST’s settings, message converters, exception handling, and more, use the @RepositoryRestController annotation instead of a standard Spring MVC @Controller or @RestController. Controllers annotated with @RepositoryRestController are served from the API base path defined in RepositoryRestConfiguration.setBasePath, which is used by all other RESTful endpoints (for example, /api).


In order to simplify the generation of HATEOAS resources (with the _links you mentioned) you can take advantage of ResourceAssemblerSupport.

As the mapping from an entity to a resource type will have to be used in multiple places it makes sense to create a dedicated class responsible for doing so. The conversion will of course contain very custom steps but also a few boilerplate ones. (...) Spring Hateoas now provides a ResourceAssemblerSupport base class that helps reducing the amount of code needed to be written

Spring Boot - Introduction, platform which paved the way for a faster and more efficient development eco-system. Spring Boot is an open source Java-based framework used to create a micro Service. It is developed by Pivotal Team and is used to build stand-alone and production ready spring applications.


You've wretched control of the ResponseEntity so it isn't build automatically for you anymore. That means you have to use LinkBuilder or ControllerLinkBuilder to build the links associated with those ResponseEntity's your self.

Docs and examples here: https://docs.spring.io/spring-hateoas/docs/current/reference/html/#fundamentals.obtaining-links.entity-links

a quick sample:

@Getter 
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(GymMembershipController.class).all(id)).withRel("memberships"));
        add(linkTo(methodOn(PersonController.class).get(id)).withSelfRel());
    }
}

From this most excellent tut: https://dzone.com/articles/applying-hateoas-to-a-rest-api-with-spring-boot

A Comparison Between Spring and Spring Boot, Spring Boot auto-configuration attempts to automatically configure your Spring application based on the jar dependencies that you have added. For example, if​  As you read more Spring Getting Started guides, you will see more use cases for Spring Boot. This guide is meant to give you a quick taste of Spring Boot. If you want to create your own Spring Boot-based project, visit Spring Initializr, fill in your project details, pick your options, and download a bundled up project as a zip file.


The return types on your controller are not Spring HATEOAS ResourceSupport types, hence it's HAL serializers will never be invoked.

You should return either Resource<PartyMember> (for single item) or Resources<Resource<PartyMember>> (for lists). Structure your controller to return these.

Make these changes, and then the concept of a reusable ResourceAssembler<PartyMember, Resource<PartyMember>> will make perfect sense as a way to transform PartyMember objects found in the service layer into Resource<PartyMember> objects used to render hypermedia.

To see this evolution of a REST-based service, check out this tutorial (which I rewrote just a few months ago to properly show usage of Spring HATEAOS) => https://spring.io/guides/tutorials/rest/

Understanding the Basics of Spring vs. Spring Boot, Spring Boot Actuator is a sub-project of Spring Boot. It adds several production grade services to your application with little effort on your part. In this guide, you  Our Spring Boot Tutorial includes all topics of Spring Boot such, as features, project, maven project, starter project wizard, Spring Initializr, CLI, applications, annotations, dependency management, properties, starters, Actuator, JPA, JDBC, etc. What is Spring Boot. Spring Boot is a project that is built on the top of the Spring Framework.


Spring Boot Tutorial – IBM Developer, Spring Boot - Introduction - Spring Boot is an open source Java-based framework used to create a micro Service. It is developed by Pivotal Team and is used to  Download Spring Boot for free. Easily create Spring-powered, production-grade applications. Spring Boot lets you create stand-alone, production-grade, Spring-based applications and services with minimal fuss. It offers a radically faster and highly accessible manner for starting all Spring development.


Getting Started, Spring Boot makes it easy to create Spring-powered, production-grade applications and services with absolute minimum fuss. It takes an opinionated view of the  According to the Spring Boot Project, Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run." What it does is scan the class path and configured beans of the application classes and makes an assessment of the missing items adds it into the project structure, all without the programmer


Spring Boot Reference Documentation, Spring Boot is an open-source micro framework maintained by a company called Pivotal. It provides Java developers with a platform to get  Spring Boot is an open-source micro framework maintained by a company called Pivotal. It provides Java developers with a platform to get started with an auto configurable production-grade Spring application.