Чтобы помочь в преобразовании объектов 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());