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 Set users;
} А затем установите отношение “многие ко многим” между Пользователем и Ролью :
@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, который можно импортировать и запускать как есть.