Выбрать главу

Чтобы помочь в преобразовании объектов Taco в объекты TacoResource, вы также собираетесь создать ассемблер ресурсов. Следующий список - это то, что вам нужно.

Листинг 6.6. Ассемблер ресурсов, который собирает тако-ресурсы

package tacos.web.api;

import org.springframework.hateoas.mvc.ResourceAssemblerSupport;

import tacos.Taco;

 

public class TacoResourceAssembler

   extends ResourceAssemblerSupport<Taco, TacoResource> {

 public TacoResourceAssembler() {

   super(DesignTacoController.class, TacoResource.class);

 }

 

@Override

 protected TacoResource instantiateResource(Taco taco) {

   return new TacoResource(taco);

 }

 

 @Override

 public TacoResource toResource(Taco taco) {

   return createResourceWithId(taco.getId(), taco);

 }

}

TacoResourceAssembler имеет конструктор по умолчанию, который сообщает суперклассу (ResourceAssemblerSupport), что он будет использовать DesignTacoController для определения базового пути для любых URL-адресов в ссылках, которые он создает при создании TacoResource.

Метод instantiateResource() переопределяется для создания экземпляра TacoResource с данным Taco. Этот метод необязательный, если TacoResource имеет конструктор по умолчанию. В этом случае, однако, TacoResource  требует для построения Taco, поэтому вы должны переопределить его.

Метод toResource() является единственным методом, строго обязательным при расширении ResourceAssemblerSupport. Здесь вы говорите ему создать объект TacoResource из Taco и автоматически дать ему собственную ссылку с URL-адресом, полученным из свойства id объекта Taco.

На первый взгляд, toResource(), похоже, имеет аналогичное назначение что и instantiateResource(), но они служат несколько иным целям. В то время как instantiateResource() предназначен только для создания экземпляра объекта Resource, метод toResource() предназначен не только для создания объекта Resource, но и для заполнения его ссылками. Под капотом toResource() находиться вызов instantiateResource().

Теперь настройте метод recentTacos(), чтобы использовать TacoResourceAssembler:

@GetMapping("/recent")

public Resources<TacoResource> recentTacos() {

 PageRequest page = PageRequest.of(

     0, 12, Sort.by("createdAt").descending());

 List<Taco> tacos = tacoRepo.findAll(page).getContent();

 List<TacoResource> tacoResources =

   new TacoResourceAssembler().toResources(tacos);

 Resources<TacoResource> recentResources =

   new Resources<TacoResource>(tacoResources);

 recentResources.add(

   linkTo(methodOn(DesignTacoController.class).recentTacos())

       .withRel("recents"));

 return recentResources;

}

Вместо того чтобы возвращать Resources<Resource<Taco >>, recentTacos() теперь возвращает Resources<TacoResource>, чтобы воспользоваться вашим новым типом TacoResource. После извлечения тако из репозитория вы передаете список объектов Taco методу toResources() класса TacoResourceAssembler. Этот удобный метод циклически перебирает все объекты Taco, вызывая метод toResource(), который вы переопределили в TacoResourceAssembler, чтобы создать список объектов TacoResource.

Используюя этот список TacoResource вы затем создаете объект Resources<TacoResource>, а затем заполняете его ссылкой на recents, как и в предыдущей версии recentTacos().

На этом этапе GET-запрос /design/ recent создаст список тако, каждый из которых имеет self ссылку и recents ссылку в самом списке. Но ингредиенты все равно останутся без ссылок. Чтобы решить эту проблему, вы создадите новый ассемблер ресурсов для ингредиентов:

package tacos.web.api;

import org.springframework.hateoas.mvc.ResourceAssemblerSupport;

import tacos.Ingredient;

 

 class IngredientResourceAssembler extends

       ResourceAssemblerSupport<Ingredient, IngredientResource> {

 public IngredientResourceAssembler() {

   super(IngredientController2.class, IngredientResource.class);

 }

 

 @Override

 public IngredientResource toResource(Ingredient ingredient) {

   return createResourceWithId(ingredient.getId(), ingredient);

 }

 

 @Override

 protected IngredientResource instantiateResource(

       Ingredient ingredient) {

   return new IngredientResource(ingredient);

 }

}

Как видите, IngredientResourceAssembler очень похож на TacoResourceAssembler, но работает с объектами Ingredient и IngredientResource вместо объектов Taco и TacoResource.

Говоря о IngredientResource,  он выглядит так:

package tacos.web.api;

import org.springframework.hateoas.ResourceSupport;

import lombok.Getter;

import tacos.Ingredient;

import tacos.Ingredient.Type;

 

public class IngredientResource extends ResourceSupport {

 @Getter

 private String name;

 @Getter

 private Type type;

 

 public IngredientResource(Ingredient ingredient) {

   this.name = ingredient.getName();

   this.type = ingredient.getType();

 }

}

Как и в случае с TacoResource, IngredientResource расширяет ResourceSupport и копирует соответствующие свойства из типа домена в свой собственный набор свойств (исключая свойство id).

Осталось лишь немного изменить TacoResource, чтобы он содержал объекты IngredientResource вместо объектов Ingredient:

package tacos.web.api;

import java.util.Date;

import java.util.List;

import org.springframework.hateoas.ResourceSupport;

import lombok.Getter;

import tacos.Taco;

 

public class TacoResource extends ResourceSupport {

 private static final IngredientResourceAssembler ingredientAssembler = new IngredientResourceAssembler();

 

 @Getter

 private final String name;

 @Getter

 private final Date createdAt;

 @Getter

 private final List<IngredientResource> ingredients;

 

 public TacoResource(Taco taco) {

   this.name = taco.getName();

   this.createdAt = taco.getCreatedAt();

   this.ingredients =ingredientAssembler.toResources(taco.getIngredients());