1. Обзор
В этой статье мы начнем изучать | JSON-API spec и то, как это может быть интегрировано в Spring backend REST API.
Мы будем использовать Katharsis реализацию JSON-API в Java – и мы настроим приложение Spring на базе Katharsis – так что все, что нам нужно, – это приложение Spring.
2. Maven
Во – первых, давайте взглянем на нашу конфигурацию maven-нам нужно добавить следующую зависимость в наш pom.xml :
io.katharsis katharsis-spring 3.0.2
3. Пользовательский ресурс
Далее давайте взглянем на наш пользовательский ресурс:
@JsonApiResource(type = "users") public class User { @JsonApiId private Long id; private String name; private String email; }
Обратите внимание, что:
- @JsonApiResource аннотация используется для определения нашего ресурса Пользователя
- @@Jsonapi аннотация используется для определения идентификатора ресурса
И очень кратко – постоянство для этого примера будет здесь хранилищем данных Spring:
public interface UserRepository extends JpaRepository{}
4. Хранилище Ресурсов
Далее давайте обсудим наш репозиторий ресурсов – каждый ресурс должен иметь ResourceRepositoryV2 для публикации доступных на нем операций API:
@Component public class UserResourceRepository implements ResourceRepositoryV2{ @Autowired private UserRepository userRepository; @Override public User findOne(Long id, QuerySpec querySpec) { Optional user = userRepository.findById(id); return user.isPresent()? user.get() : null; } @Override public ResourceList findAll(QuerySpec querySpec) { return querySpec.apply(userRepository.findAll()); } @Override public ResourceList findAll(Iterable ids, QuerySpec querySpec) { return querySpec.apply(userRepository.findAllById(ids)); } @Override public S save(S entity) { return userRepository.save(entity); } @Override public void delete(Long id) { userRepository.deleteById(id); } @Override public ClassgetResourceClass() { return User.class; } @Override public S create(S entity) { return save(entity); } }
Краткое примечание здесь – это, конечно, очень похоже на пружинный контроллер .
5. Конфигурация Катарсиса
Поскольку мы используем katharsis-spring , все, что нам нужно сделать, это импортировать KatharsisConfigV3 в наше приложение Spring Boot:
@Import(KatharsisConfigV3.class)
И настройте параметры Katharsis в нашем application.properties :
katharsis.domainName=http://localhost:8080 katharsis.pathPrefix=/
С этим – теперь мы можем начать использовать API; например:
- ПОЛУЧИТЬ ” http://localhost:8080/users “: чтобы получить всех пользователей.
- СООБЩЕНИЕ ” http://localhost:8080/users “: добавить нового пользователя и многое другое.
6. Отношения
Далее давайте обсудим, как обрабатывать отношения сущностей в нашем API JSON.
6.1. Ролевой ресурс
Во – первых, давайте представим новый ресурс – Роль :
@JsonApiResource(type = "roles") public class Role { @JsonApiId private Long id; private String name; @JsonApiRelation private Setusers; }
А затем установите отношение “многие ко многим” между Пользователем и Ролью :
@JsonApiRelation(serialize=SerializeType.EAGER) private Setroles;
6.2. Хранилище ресурсов ролей
Очень быстро – вот наш Ролевой репозиторий ресурсов:
@Component public class RoleResourceRepository implements ResourceRepositoryV2{ @Autowired private RoleRepository roleRepository; @Override public Role findOne(Long id, QuerySpec querySpec) { Optional role = roleRepository.findById(id); return role.isPresent()? role.get() : null; } @Override public ResourceList findAll(QuerySpec querySpec) { return querySpec.apply(roleRepository.findAll()); } @Override public ResourceList findAll(Iterable ids, QuerySpec querySpec) { return querySpec.apply(roleRepository.findAllById(ids)); } @Override public S save(S entity) { return roleRepository.save(entity); } @Override public void delete(Long id) { roleRepository.deleteById(id); } @Override public ClassgetResourceClass() { return Role.class; } @Override public S create(S entity) { return save(entity); } }
Здесь важно понимать, что это единственное репо ресурсов не обрабатывает аспект отношений – для этого требуется отдельный репозиторий.
6.3. Хранилище отношений
Чтобы справиться с отношениями “многие ко многим” между Пользователем – Ролью , нам нужно создать новый стиль репозитория:
@Component public class UserToRoleRelationshipRepository implements RelationshipRepositoryV2{ @Autowired private UserRepository userRepository; @Autowired private RoleRepository roleRepository; @Override public void setRelation(User User, Long roleId, String fieldName) {} @Override public void setRelations(User user, Iterable roleIds, String fieldName) { Set roles = new HashSet (); roles.addAll(roleRepository.findAllById(roleIds)); user.setRoles(roles); userRepository.save(user); } @Override public void addRelations(User user, Iterable roleIds, String fieldName) { Set roles = user.getRoles(); roles.addAll(roleRepository.findAllById(roleIds)); user.setRoles(roles); userRepository.save(user); } @Override public void removeRelations(User user, Iterable roleIds, String fieldName) { Set roles = user.getRoles(); roles.removeAll(roleRepository.findAllById(roleIds)); user.setRoles(roles); userRepository.save(user); } @Override public Role findOneTarget(Long sourceId, String fieldName, QuerySpec querySpec) { return null; } @Override public ResourceList findManyTargets(Long sourceId, String fieldName, QuerySpec querySpec) { final Optional userOptional = userRepository.findById(sourceId); User user = userOptional.isPresent() ? userOptional.get() : new User(); return querySpec.apply(user.getRoles()); } @Override public Class getSourceResourceClass() { return User.class; } @Override public Class getTargetResourceClass() { return Role.class; } }
Мы игнорируем сингулярные методы здесь, в хранилище отношений.
7. Тест
Наконец, давайте проанализируем несколько запросов и действительно поймем, как выглядит вывод JSON-API.
Мы собираемся начать извлечение одного пользовательского ресурса (с):
ПОЛУЧИТЬ http://localhost:8080/users/2
{ "data":{ "type":"users", "id":"2", "attributes":{ "email":"[email protected]", "username":"tom" }, "relationships":{ "roles":{ "links":{ "self":"http://localhost:8080/users/2/relationships/roles", "related":"http://localhost:8080/users/2/roles" } } }, "links":{ "self":"http://localhost:8080/users/2" } }, "included":[ { "type":"roles", "id":"1", "attributes":{ "name":"ROLE_USER" }, "relationships":{ "users":{ "links":{ "self":"http://localhost:8080/roles/1/relationships/users", "related":"http://localhost:8080/roles/1/users" } } }, "links":{ "self":"http://localhost:8080/roles/1" } } ] }
Еда на вынос:
- Основные атрибуты ресурса находятся в data.attributes
- Основные отношения ресурса находятся в data.relations
- Как мы использовали @JsonApiRelation(serialize=Тип сериализации.() для отношений ролей он включен в JSON и находится в узле included
Далее – давайте получим ресурс коллекции, содержащий роли:
ПОЛУЧИТЬ http://localhost:8080/roles
{ "data":[ { "type":"roles", "id":"1", "attributes":{ "name":"ROLE_USER" }, "relationships":{ "users":{ "links":{ "self":"http://localhost:8080/roles/1/relationships/users", "related":"http://localhost:8080/roles/1/users" } } }, "links":{ "self":"http://localhost:8080/roles/1" } }, { "type":"roles", "id":"2", "attributes":{ "name":"ROLE_ADMIN" }, "relationships":{ "users":{ "links":{ "self":"http://localhost:8080/roles/2/relationships/users", "related":"http://localhost:8080/roles/2/users" } } }, "links":{ "self":"http://localhost:8080/roles/2" } } ], "included":[ ] }
Быстрый вывод здесь заключается в том, что мы получаем все роли в системе- в виде массива в узле data
8. Заключение
JSON-API – это фантастическая спецификация, наконец-то добавляющая некоторую структуру в то, как мы используем JSON в наших API, и действительно приводящая в действие настоящий Гипермедиа API.
В этой статье рассматривается один из способов настроить его в приложении Spring. Но независимо от этой реализации, сама спецификация, на мой взгляд, очень многообещающая работа.
Полный исходный код для примера доступен через на GitHub . Это проект Maven, который можно импортировать и запускать как есть.