JSON API в приложении Spring
1. обзор
2. специалист
Во-первых, давайте взглянем на нашу конфигурацию 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 используется для определения нашего ресурсаUser
-
Аннотация@JsonApiId используется для определения идентификатора ресурса
И очень кратко - постоянство для этого примера будет здесь репозиторием Spring Data:
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) {
return userRepository.findOne(id);
}
@Override
public ResourceList findAll(QuerySpec querySpec) {
return querySpec.apply(userRepository.findAll());
}
@Override
public ResourceList findAll(Iterable ids, QuerySpec querySpec) {
return querySpec.apply(userRepository.findAll(ids));
}
@Override
public S save(S entity) {
return userRepository.save(entity);
}
@Override
public void delete(Long id) {
userRepository.delete(id);
}
@Override
public Class getResourceClass() {
return User.class;
}
@Override
public S create(S entity) {
return save(entity);
}
}
Небольшое примечание - это, конечно,very similar to a Spring controller.
5. Конфигурация Катарсиса
Поскольку мы используемkatharsis-spring, все, что нам нужно сделать, это импортироватьKatharsisConfigV3 в наше приложение Spring Boot:
@Import(KatharsisConfigV3.class)
И настройте параметры Катарсиса в нашемapplication.properties:
katharsis.domainName=http://localhost:8080
katharsis.pathPrefix=/
Теперь мы можем начать использовать API; например:
-
GET «http://localhost:8080/users«: получить всех пользователей.
-
POST «http://localhost:8080/users»: для добавления нового пользователя и т. Д.
6. Отношения
Затем давайте обсудим, как обрабатывать отношения сущностей в нашем JSON API.
6.1. Ресурс роли
Во-первых, давайте представим новый ресурс -Role:
@JsonApiResource(type = "roles")
public class Role {
@JsonApiId
private Long id;
private String name;
@JsonApiRelation
private Set users;
}
А затем установите отношение «многие ко многим» междуUser иRole:
@JsonApiRelation(serialize=SerializeType.EAGER)
private Set roles;
6.2. Репозиторий ресурсов роли
Очень быстро - вот наш репозиторий ресурсовRole:
@Component
public class RoleResourceRepository implements ResourceRepositoryV2 {
@Autowired
private RoleRepository roleRepository;
@Override
public Role findOne(Long id, QuerySpec querySpec) {
return roleRepository.findOne(id);
}
@Override
public ResourceList findAll(QuerySpec querySpec) {
return querySpec.apply(roleRepository.findAll());
}
@Override
public ResourceList findAll(Iterable ids, QuerySpec querySpec) {
return querySpec.apply(roleRepository.findAll(ids));
}
@Override
public S save(S entity) {
return roleRepository.save(entity);
}
@Override
public void delete(Long id) {
roleRepository.delete(id);
}
@Override
public Class getResourceClass() {
return Role.class;
}
@Override
public S create(S entity) {
return save(entity);
}
}
Здесь важно понимать, что это единое хранилище ресурсов не обрабатывает аспект отношений - для этого требуется отдельный репозиторий.
6.3. Репозиторий отношений
Чтобы обрабатывать отношения «многие ко многим» междуUser -Role, нам необходимо создать новый стиль репозитория:
@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.findAll(roleIds));
user.setRoles(roles);
userRepository.save(user);
}
@Override
public void addRelations(User user, Iterable roleIds, String fieldName) {
Set roles = user.getRoles();
roles.addAll(roleRepository.findAll(roleIds));
user.setRoles(roles);
userRepository.save(user);
}
@Override
public void removeRelations(User user, Iterable roleIds, String fieldName) {
Set roles = user.getRoles();
roles.removeAll(roleRepository.findAll(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) {
User user = userRepository.findOne(sourceId);
return querySpec.apply(user.getRoles());
}
@Override
public Class getSourceResourceClass() {
return User.class;
}
@Override
public Class getTargetResourceClass() {
return Role.class;
}
}
Мы игнорируем особые методы здесь, в репозитории отношений.
7. Test
Наконец, давайте проанализируем несколько запросов и действительно поймем, как выглядит вывод JSON-API.
Мы собираемся начать получение одного ресурса User (с id = 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"
}
}
]
}
Takeaways:
-
Основные атрибуты ресурса находятся вdata.attributes
-
Основные отношения Ресурса находятся вdata.relationships
-
Поскольку мы использовали@JsonApiRelation(serialize=SerializeType.EAGER) для отношенияroles, он включен в JSON и находится в узлеincluded
Далее - давайте получим ресурс коллекции, содержащий роли:
{
"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-интерфейсах, и действительно обеспечим истинный Hypermedia API.
В этом материале был рассмотрен один из способов его установки в приложении Spring. Но независимо от этой реализации, сама спецификация, на мой взгляд, очень и очень многообещающая работа.
Полный исходный код примера доступен черезon GitHub. Это проект Maven, который можно импортировать и запускать как есть.