In reading the recent Spring combat the fifth edition of the Chinese version, in Chapter 6 on Spring HATEOAS part of the code using the Spring HATEOAS version 0.25, but the latest Spring HATEOAS 1.0 to your legacy API that do the upgrade, resulting in the use of the new Spring Boot (as of articles published the latest version of day Spring Boot 2.2.4) loaded Spring HATEOAS 1.0.3 code is not running in the book, so I decided on this migration upgrade the code book.
Read this part of the book online: https://potoyang.gitbook.io/spring-in-action-v5/di-6-zhang-chuang-jian-rest-fu-wu/6.2-qi-yong-chao-mei -ti
Spring HATEOAS 1.0 version of the change
The biggest change package structure by introducing hypermedia type registration API is implemented to support other media types in Spring HATEOAS. This causes the client API and server API (named package) package and a clear separation of media types implemented mediatype
.
The biggest change is the original resource represented as a model, specific changes are as follows.
In ResourceSupport
/ Resource
/ Resources
/ PagedResources
group class never really feel appropriately named. After all, in fact, does not mean that these types of resources, but rather models that can enrich them by hypermedia information and content provided. This is the new name mapped to the old name of ways:
-
ResourceSupport
It's nowRepresentationModel
-
Resource
It's nowEntityModel
-
Resources
It's nowCollectionModel
-
PagedResources
It's nowPagedModel
Thus, ResourceAssembler
it has been renamed RepresentationModelAssembler
and method thereof toResource(…)
, and eachtoResources(…)
was renamed toModel(…)
and toCollectionModel(…)
. Name change is also reflected in the class containedTypeReferences
.
-
RepresentationModel.getLinks()
Now discloses anLinks
example of (throughList<Link>
), the examples disclosed in the API of the other, toLinks
use various strategies to connect and merge a different instance. Similarly, it has become a generic type from binding to approach allows to add links to the instance returns an instance of itself. -
The
LinkDiscoverer
API is moved to theclient
package. -
In
LinkBuilder
andEntityLinks
API have been moved to theserver
package. -
ControllerLinkBuilder
Has been movedserver.mvc
, it is not recommended to replaceWebMvcLinkBuilder
. -
RelProvider
It has been renamedLinkRelationProvider
and returnedLinkRelation
instance insteadString
. -
VndError
It has been moved tomediatype.vnderror
suite.
Spring HATEOAS more changes please refer to the documentation: https://spring.io/projects/spring-hateoas
Code Migration Upgrade
The book Listing 6.4 add hyperlinks to resources
@GetMapping("/recent") public CollectionModel<EntityModel<Taco>> recentTacos() { PageRequest page = PageRequest.of( 0, 12, Sort.by("createdAt").descending()); List<Taco> tacos = tacoRepo.findAll(page).getContent(); CollectionModel<EntityModel<Taco>> recentResources = CollectionModel.wrap(tacos); recentResources.add( new Link("http://localhost:8080/design/recent", "recents")); return recentResources; }
Eliminate hard-coded URL
@GetMapping("/recent") public CollectionModel<EntityModel<Taco>> recentTacos() { PageRequest page = PageRequest.of( 0, 12, Sort.by("createdAt").descending()); List<Taco> tacos = tacoRepo.findAll(page).getContent(); CollectionModel<EntityModel<Taco>> recentResources = CollectionModel.wrap(tacos); recentResources.add( linkTo(methodOn(DesignTacoController.class).recentTacos()).withRel("recents")); return recentResources; }
public class TacoResource extends RepresentationModel<TacoResource> { @Getter private String name; @Getter private Date createdAt; @Getter private List<Ingredient> ingredients; public TacoResource(Taco taco) { this.name = taco.getName(); this.createdAt = taco.getCreatedAt(); this.ingredients = taco.getIngredients(); } }
public class TacoResourceAssembler extends RepresentationModelAssemblerSupport<Taco, TacoResource> { /** * Creates a new {@link RepresentationModelAssemblerSupport} using the given controller class and resource type. * * @param controllerClass DesignTacoController {@literal DesignTacoController}. * @param resourceType TacoResource {@literal TacoResource}. */ public TacoResourceAssembler(Class<?> controllerClass, Class<TacoResource> resourceType) { super(controllerClass, resourceType); } @Override protected TacoResource instantiateModel(Taco taco) { return new TacoResource(taco); } @Override public TacoResource toModel(Taco entity) { return createModelWithId(entity.getId(), entity); } }
After adjusting for recentTacos () of
@GetMapping("/recentNew") public CollectionModel<TacoResource> recentTacos() { PageRequest page = PageRequest.of( 0, 12, Sort.by("createdAt").descending()); List<Taco> tacos = tacoRepo.findAll(page).getContent(); CollectionModel<TacoResource> tacoResources = new TacoResourceAssembler(DesignTacoController.class, TacoResource.class).toCollectionModel(tacos); tacoResources.add(linkTo(methodOn(DesignTacoController.class) .recentTacos()) .withRel("recents")); return tacoResources; }
Objects created IngredientResource
@Data public class IngredientResource extends RepresentationModel<IngredientResource> { public IngredientResource(Ingredient ingredient) { this.name = ingredient.getName(); this.type = ingredient.getType(); } private final String name; private final Ingredient.Type type; }
public class IngredientResourceAssembler extends RepresentationModelAssemblerSupport<Ingredient, IngredientResource> { /** * Creates a new {@link RepresentationModelAssemblerSupport} using the given controller class and resource type. * * @param controllerClass IngredientController {@literal IngredientController}. * @param resourceType IngredientResource {@literal IngredientResource}. */ public IngredientResourceAssembler(Class<?> controllerClass, Class<IngredientResource> resourceType) { super(controllerClass, resourceType); } @Override protected IngredientResource instantiateModel(Ingredient entity) { return new IngredientResource(entity); } @Override public IngredientResource toModel(Ingredient entity) { return createModelWithId(entity.getId(), entity); } }
Modification of the object TacoResource
public class TacoResource extends RepresentationModel<TacoResource> { private static final IngredientResourceAssembler ingredientAssembler = new IngredientResourceAssembler(IngredientController.class, IngredientResource.class); @Getter private String name; @Getter private Date createdAt; @Getter private CollectionModel<IngredientResource> ingredients; public TacoResource(Taco taco) { this.name = taco.getName(); this.createdAt = taco.getCreatedAt(); this.ingredients = ingredientAssembler.toCollectionModel(taco.getIngredients()); } }
Listing 6.7
@RepositoryRestController public class RecentTacosController { Private TacoRepository tacoRepo; public RecentTacosController (TacoRepository tacoRepo) { the this .tacoRepo = tacoRepo; } / ** * While @GetMapping mapped to "/ tacos / recent" path, but the class level @Repository RestController annotations ensure that the path to add the base path * Spring Data REST as a prefix. According to our configuration, recentTacos () method will handle the request GET for "/ api / tacos / recent" is. * * / @GetMapping (path = "/ Tacos / Recent", Produces = "file application / JSON + HAL" ) public ResponseEntity <CollectionModel <TacoResource >> recentTacos () { PageRequest Page = PageRequest.of( 0, 12, Sort.by("createdAt").descending()); List<Taco> tacos = tacoRepo.findAll(page).getContent(); CollectionModel<TacoResource> tacoResources = new TacoResourceAssembler(DesignTacoController.class, TacoResource.class).toCollectionModel(tacos); tacoResources.add( linkTo(methodOn(RecentTacosController.class).recentTacos()) .withRel("recents")); return new ResponseEntity<>(tacoResources, HttpStatus.OK); } }
Another way
If you think the use of the resource assembler to write a little trouble, then you can also adopt this approach.
@GetMapping("/employees") public ResponseEntity<CollectionModel<EntityModel<Taco>>> findAll() { PageRequest page = PageRequest.of( 0, 12, Sort.by("createdAt").descending()); List<EntityModel<Taco>> employees = StreamSupport.stream(tacoRepo.findAll(page).spliterator(), false) .map(employee -> new EntityModel<>(employee, linkTo(methodOn(DesignTacoController.class).findOne(employee.getId())).withSelfRel(), linkTo (methodOn (DesignTacoController.class).findAll()).withRel("employees"))) .collect(Collectors.toList()); return ResponseEntity.ok( new CollectionModel<>(employees, linkTo(methodOn(DesignTacoController.class).findAll()).withSelfRel())); }
@GetMapping("/employees/{id}") public ResponseEntity<EntityModel<Taco>> findOne(@PathVariable long id) { return tacoRepo.findById(id) .map(employee -> new EntityModel<>(employee, linkTo(methodOn(DesignTacoController.class).findOne(employee.getId())).withSelfRel(), // linkTo(methodOn(DesignTacoController.class).findAll()).withRel("employees"))) // .map(ResponseEntity::ok) .orElse(ResponseEntity.notFound().build()); }
Reference Source: https://github.com/spring-projects/spring-hateoas-examples/tree/master/simplified
END