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

Используя ControllerLinkBuilder, вы можете переписать хардкордное задание Link в RecentTacos() следующими строками:

Resources<Resource<Taco>> recentResources = Resources.wrap(tacos);

recentResources.add(

 ControllerLinkBuilder.linkTo(DesignTacoController.class)

   .slash("recent")

   .withRel("recents"));

Вам не только больше не нужно хардкодить имя хоста, вам также не нужно указывать путь /design. Вместо этого вы запрашиваете ссылку на DesignTacoController, базовый путь которого /design. ControllerLinkBuilder использует базовый путь контроллера в качестве основы создаваемого вами объекта Link.

Далее следует вызов одного из моих любимых методов в любом Spring проекте : slash (). Мне нравится этот метод, потому что он так кратко описывает, что именно он собирается делать. Он буквально добавляет косую черту (/) и заданное значение в URL. В результате путь URL-адреса /design/recent.

Наконец, вы указываете имя отношения для ссылки. В этом примере отношение называется recents.

Хотя я большой поклонник метода slash(), у ControllerLinkBuilder есть еще один метод, который может помочь устранить любое жесткое кодирование, связанное с URL-адресами ссылок. Вместо того, чтобы вызывать slash(), вы можете вызвать linkTo(), передав ему в метод контроллер, чтобы ControllerLinkBuilder получал базовый URL как из базового пути контроллера, так и из сопоставленного пути метода. Следующий код написан с использованием метода linkTo():

Resources<Resource<Taco>> recentResources = Resources.wrap(tacos);

recentResources.add(

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

   .withRel("recents"));

Здесь я решил статически включить методы linkTo() и methodOn() (оба из ControllerLinkBuilder), чтобы облегчить чтение кода. Метод methodOn() берет класс контроллера и позволяет вам вызвать метод recentTacos(), который перехватывается ControllerLinkBuilder и используется для определения не только базового пути контроллера, но и пути, сопоставленного с recentTacos(). Теперь весь URL-адрес получен из сопоставлений контроллера, и нет никакого хардкода. Великолепно!

6.2.2 Создание ресурсов ассемблеров (assemblers)

Теперь вам нужно добавить ссылки на ресурс тако, содержащийся в списке. Один из вариантов - циклически проходить по каждому из элементов Resource<Taco>, содержащихся в объекте Resources, добавляя ссылку на каждый из них по отдельности. Но это немного утомительно, и вам нужно будет повторять этот код цикла в API везде, где вы возвращаете список ресурсов тако.

Нам нужна другая тактика.

Вместо того чтобы позволить Resources.wrap () создать объект Resource для каждого тако в списке, вы определите служебный класс, который преобразует объекты Taco в новый объект TacoResource. Объект TacoResource будет очень похож на Taco, но он также будет иметь возможность переносить ссылки. Следующий листинг показывает, как может выглядеть TacoResource.

Листинг 6.5. Тако-ресурс, несущий данные домена и список гиперссылок

package tacos.web.api;

import java.util.Date;

import java.util.List;

import org.springframework.hateoas.ResourceSupport;

import lombok.Getter;

import tacos.Ingredient;

import tacos.Taco;

 

public class TacoResource extends ResourceSupport {

 @Getter

 private final String name;

 @Getter

 private final Date createdAt;

 @Getter

 private final List<Ingredient> ingredients;

 

 public TacoResource(Taco taco) {

   this.name = taco.getName();

   this.createdAt = taco.getCreatedAt();

   this.ingredients = taco.getIngredients();

 }

}

Во многом TacoResource ничем не отличается от доменного типа Taco. У них обоих есть свойства name, createAt и ingredients. Но TacoResource расширяет ResourceSupport для наследования списка объектов Link и методов для управления списком ссылок.

Более того, TacoResource не включает свойство id из Taco. Это потому, что нет необходимости предоставлять какие-либо специфичные для базы данных идентификаторы в API. Самостоятельная ссылка ресурса будет служить идентификатором ресурса с точки зрения клиента API.

ПРИМЕЧАНИЕ Домены и ресурсы: отдельные или одинаковые? Некоторые разработчики Spring могут объединить свои доменные типы и ресурсные в один тип, если их типы доменов расширяют ResourceSupport. Так тоже можно, нет правильного или неправильного ответа относительно того, какой путь правильный. Я выбрал создание отдельного типа ресурса, чтобы в Taco не было ненужных загромождений ссылками на ресурсы в тех случаях, когда ссылки не нужны. Кроме того, создав отдельный тип ресурса, я смог легко опустить свойство id, чтобы оно не отображалось в API.

TacoResource имеет единственный конструктор, который принимает Taco и копирует соответствующие свойства из Taco в его собственные свойства. Это облегчает преобразование объекта Taco в TacoResource. Но если вы остановитесь на этом, вам все равно понадобится цикл для преобразования списка объектов Taco в Resources<TacoResource>.