1. 概述
在本文中,我們將開始探索 JSON-API 規範,以及如何將其集成到 Spring 支持的 REST API 中。
我們將使用 Katharsis 在 Java 中實現 JSON-API – 並且我們將設置一個使用 Katharsis 的 Spring 應用程序,因此我們只需要一個 Spring 應用程序。
2. Maven
首先,讓我們來看一下我們的 Maven 配置——我們需要將以下依賴項添加到我們的 pom.xml
<dependency>
<groupId>io.katharsis</groupId>
<artifactId>katharsis-spring</artifactId>
<version>3.0.2</version>
</dependency>
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<User, Long> {}
4. 資源倉庫
接下來,我們討論一下我們的資源倉庫——每個資源都應該有一個 ResourceRepositoryV2 以發佈其可用的API操作:
@Component
public class UserResourceRepository implements ResourceRepositoryV2<User, Long> {
@Autowired
private UserRepository userRepository;
@Override
public User findOne(Long id, QuerySpec querySpec) {
Optional<User> user = userRepository.findById(id);
return user.isPresent()? user.get() : null;
}
@Override
public ResourceList<User> findAll(QuerySpec querySpec) {
return querySpec.apply(userRepository.findAll());
}
@Override
public ResourceList<User> findAll(Iterable<Long> ids, QuerySpec querySpec) {
return querySpec.apply(userRepository.findAllById(ids));
}
@Override
public <S extends User> S save(S entity) {
return userRepository.save(entity);
}
@Override
public void delete(Long id) {
userRepository.deleteById(id);
}
@Override
public Class<User> getResourceClass() {
return User.class;
}
@Override
public <S extends User> S create(S entity) {
return save(entity);
}
}
請注意,這當然與Spring控制器非常相似。
5. Katharsis 配置
由於我們使用了 katharsis-spring,我們只需要在我們的 Spring Boot 應用程序中導入 KatharsisConfigV3:
@Import(KatharsisConfigV3.class)
並且在我們的 application.properties 中配置 Katharsis 參數:
katharsis.domainName=http://localhost:8080
katharsis.pathPrefix=/
有了這些 – 我們現在可以開始消費 API;例如:
- GET “http://localhost:8080/users“: 獲取所有用户.
- POST “http://localhost:8080/users“: 添加新用户,以及更多.
6. Relationships
接下來,我們討論一下如何在 JSON API 中處理實體之間的關係。
6.1. Role Resource
首先,我們介紹一個新資源——Role:
@JsonApiResource(type = "roles")
public class Role {
@JsonApiId
private Long id;
private String name;
@JsonApiRelation
private Set<User> users;
}
然後設置User和Role之間的多對多關係:
@JsonApiRelation(serialize=SerializeType.EAGER)
private Set<Role> roles;
6.2. Role Resource Repository
快速説明一下——我們的Role 資源倉庫:
@Component
public class RoleResourceRepository implements ResourceRepositoryV2<Role, Long> {
@Autowired
private RoleRepository roleRepository;
@Override
public Role findOne(Long id, QuerySpec querySpec) {
Optional<Role> role = roleRepository.findById(id);
return role.isPresent()? role.get() : null;
}
@Override
public ResourceList<Role> findAll(QuerySpec querySpec) {
return querySpec.apply(roleRepository.findAll());
}
@Override
public ResourceList<Role> findAll(Iterable<Long> ids, QuerySpec querySpec) {
return querySpec.apply(roleRepository.findAllById(ids));
}
@Override
public <S extends Role> S save(S entity) {
return roleRepository.save(entity);
}
@Override
public void delete(Long id) {
roleRepository.deleteById(id);
}
@Override
public Class<Role> getResourceClass() {
return Role.class;
}
@Override
public <S extends Role> S create(S entity) {
return save(entity);
}
}
需要注意的是,這個單一資源倉庫不處理關係方面——這需要一個單獨的倉庫。
6.3. Relationship Repository
為了處理User和Role之間的多對多關係,我們需要創建一個新的倉庫風格:
@Component
public class UserToRoleRelationshipRepository implements RelationshipRepositoryV2<User, Long, Role, Long> {
@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<Long> roleIds, String fieldName) {
Set<Role> roles = new HashSet<Role>();
roles.addAll(roleRepository.findAllById(roleIds));
user.setRoles(roles);
userRepository.save(user);
}
@Override
public void addRelations(User user, Iterable<Long> roleIds, String fieldName) {
Set<Role> roles = user.getRoles();
roles.addAll(roleRepository.findAllById(roleIds));
user.setRoles(roles);
userRepository.save(user);
}
@Override
public void removeRelations(User user, Iterable<Long> roleIds, String fieldName) {
Set<Role> 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<Role> findManyTargets(Long sourceId, String fieldName, QuerySpec querySpec) {
final Optional<User> userOptional = userRepository.findById(sourceId);
User user = userOptional.isPresent() ? userOptional.get() : new User();
return querySpec.apply(user.getRoles());
}
@Override
public Class<User> getSourceResourceClass() {
return User.class;
}
@Override
public Class<Role> getTargetResourceClass() {
return Role.class;
}
}
我們忽略了這裏的一些單方法,在關係倉庫中。
7. 測試
最後,讓我們分析一些請求並真正瞭解 JSON-API 輸出是什麼樣的。
我們將開始檢索單個用户資源(id = 2):
GET 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.relationships
- 由於我們使用了 @JsonApiRelation(serialize=SerializeType.EAGER) 對 roles 關係進行了配置,因此該關係包含在 JSON 中,並且位於節點 included
接下來,讓我們獲取包含角色的集合資源:
GET 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 是一項絕佳規範——它終於為我們API中的JSON添加了結構,並真正實現了超媒體API。
本文探討了一種在Spring應用中設置它的方法。但無論這種實現方式如何,我認為該規範本身非常具有前景。
接下來,我們討論一下如何在 JSON API 中處理實體之間的關係。
6.1. Role Resource
首先,我們介紹一個新資源——Role:
@JsonApiResource(type = "roles")
public class Role {
@JsonApiId
private Long id;
private String name;
@JsonApiRelation
private Set<User> users;
}
然後設置User和Role之間的多對多關係:
@JsonApiRelation(serialize=SerializeType.EAGER)
private Set<Role> roles;
6.2. Role Resource Repository
快速説明一下——我們的Role 資源倉庫:
@Component
public class RoleResourceRepository implements ResourceRepositoryV2<Role, Long> {
@Autowired
private RoleRepository roleRepository;
@Override
public Role findOne(Long id, QuerySpec querySpec) {
Optional<Role> role = roleRepository.findById(id);
return role.isPresent()? role.get() : null;
}
@Override
public ResourceList<Role> findAll(QuerySpec querySpec) {
return querySpec.apply(roleRepository.findAll());
}
@Override
public ResourceList<Role> findAll(Iterable<Long> ids, QuerySpec querySpec) {
return querySpec.apply(roleRepository.findAllById(ids));
}
@Override
public <S extends Role> S save(S entity) {
return roleRepository.save(entity);
}
@Override
public void delete(Long id) {
roleRepository.deleteById(id);
}
@Override
public Class<Role> getResourceClass() {
return Role.class;
}
@Override
public <S extends Role> S create(S entity) {
return save(entity);
}
}
需要注意的是,這個單一資源倉庫不處理關係方面——這需要一個單獨的倉庫。
6.3. Relationship Repository
為了處理User和Role之間的多對多關係,我們需要創建一個新的倉庫風格:
@Component
public class UserToRoleRelationshipRepository implements RelationshipRepositoryV2<User, Long, Role, Long> {
@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<Long> roleIds, String fieldName) {
Set<Role> roles = new HashSet<Role>();
roles.addAll(roleRepository.findAllById(roleIds));
user.setRoles(roles);
userRepository.save(user);
}
@Override
public void addRelations(User user, Iterable<Long> roleIds, String fieldName) {
Set<Role> roles = user.getRoles();
roles.addAll(roleRepository.findAllById(roleIds));
user.setRoles(roles);
userRepository.save(user);
}
@Override
public void removeRelations(User user, Iterable<Long> roleIds, String fieldName) {
Set<Role> 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<Role> findManyTargets(Long sourceId, String fieldName, QuerySpec querySpec) {
final Optional<User> userOptional = userRepository.findById(sourceId);
User user = userOptional.isPresent() ? userOptional.get() : new User();
return querySpec.apply(user.getRoles());
}
@Override
public Class<User> getSourceResourceClass() {
return User.class;
}
@Override
public Class<Role> getTargetResourceClass() {
return Role.class;
}
}
我們忽略了這裏的一些單方法,在關係倉庫中。
7. 測試
最後,讓我們分析一些請求並真正瞭解 JSON-API 輸出是什麼樣的。
我們將開始檢索單個用户資源(id = 2):
GET 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.relationships
- 由於我們使用了 @JsonApiRelation(serialize=SerializeType.EAGER) 對 roles 關係進行了配置,因此該關係包含在 JSON 中,並且位於節點 included
接下來,讓我們獲取包含角色的集合資源:
GET 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 是一項絕佳規範——它終於為我們API中的JSON添加了結構,並真正實現了超媒體API。
本文探討了一種在Spring應用中設置它的方法。但無論這種實現方式如何,我認為該規範本身非常具有前景。
首先,我們介紹一個新資源——
@JsonApiResource(type = "roles")
public class Role {
@JsonApiId
private Long id;
private String name;
@JsonApiRelation
private Set<User> users;
}
然後設置
@JsonApiRelation(serialize=SerializeType.EAGER)
private Set<Role> roles;
6.2. Role Resource Repository
快速説明一下——我們的Role 資源倉庫:
@Component
public class RoleResourceRepository implements ResourceRepositoryV2<Role, Long> {
@Autowired
private RoleRepository roleRepository;
@Override
public Role findOne(Long id, QuerySpec querySpec) {
Optional<Role> role = roleRepository.findById(id);
return role.isPresent()? role.get() : null;
}
@Override
public ResourceList<Role> findAll(QuerySpec querySpec) {
return querySpec.apply(roleRepository.findAll());
}
@Override
public ResourceList<Role> findAll(Iterable<Long> ids, QuerySpec querySpec) {
return querySpec.apply(roleRepository.findAllById(ids));
}
@Override
public <S extends Role> S save(S entity) {
return roleRepository.save(entity);
}
@Override
public void delete(Long id) {
roleRepository.deleteById(id);
}
@Override
public Class<Role> getResourceClass() {
return Role.class;
}
@Override
public <S extends Role> S create(S entity) {
return save(entity);
}
}
需要注意的是,這個單一資源倉庫不處理關係方面——這需要一個單獨的倉庫。
6.3. Relationship Repository
為了處理User和Role之間的多對多關係,我們需要創建一個新的倉庫風格:
@Component
public class UserToRoleRelationshipRepository implements RelationshipRepositoryV2<User, Long, Role, Long> {
@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<Long> roleIds, String fieldName) {
Set<Role> roles = new HashSet<Role>();
roles.addAll(roleRepository.findAllById(roleIds));
user.setRoles(roles);
userRepository.save(user);
}
@Override
public void addRelations(User user, Iterable<Long> roleIds, String fieldName) {
Set<Role> roles = user.getRoles();
roles.addAll(roleRepository.findAllById(roleIds));
user.setRoles(roles);
userRepository.save(user);
}
@Override
public void removeRelations(User user, Iterable<Long> roleIds, String fieldName) {
Set<Role> 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<Role> findManyTargets(Long sourceId, String fieldName, QuerySpec querySpec) {
final Optional<User> userOptional = userRepository.findById(sourceId);
User user = userOptional.isPresent() ? userOptional.get() : new User();
return querySpec.apply(user.getRoles());
}
@Override
public Class<User> getSourceResourceClass() {
return User.class;
}
@Override
public Class<Role> getTargetResourceClass() {
return Role.class;
}
}
我們忽略了這裏的一些單方法,在關係倉庫中。
7. 測試
最後,讓我們分析一些請求並真正瞭解 JSON-API 輸出是什麼樣的。
我們將開始檢索單個用户資源(id = 2):
GET 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.relationships
- 由於我們使用了 @JsonApiRelation(serialize=SerializeType.EAGER) 對 roles 關係進行了配置,因此該關係包含在 JSON 中,並且位於節點 included
接下來,讓我們獲取包含角色的集合資源:
GET 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 是一項絕佳規範——它終於為我們API中的JSON添加了結構,並真正實現了超媒體API。
本文探討了一種在Spring應用中設置它的方法。但無論這種實現方式如何,我認為該規範本身非常具有前景。
快速説明一下——我們的
@Component
public class RoleResourceRepository implements ResourceRepositoryV2<Role, Long> {
@Autowired
private RoleRepository roleRepository;
@Override
public Role findOne(Long id, QuerySpec querySpec) {
Optional<Role> role = roleRepository.findById(id);
return role.isPresent()? role.get() : null;
}
@Override
public ResourceList<Role> findAll(QuerySpec querySpec) {
return querySpec.apply(roleRepository.findAll());
}
@Override
public ResourceList<Role> findAll(Iterable<Long> ids, QuerySpec querySpec) {
return querySpec.apply(roleRepository.findAllById(ids));
}
@Override
public <S extends Role> S save(S entity) {
return roleRepository.save(entity);
}
@Override
public void delete(Long id) {
roleRepository.deleteById(id);
}
@Override
public Class<Role> getResourceClass() {
return Role.class;
}
@Override
public <S extends Role> S create(S entity) {
return save(entity);
}
}
需要注意的是,這個單一資源倉庫不處理關係方面——這需要一個單獨的倉庫。
6.3. Relationship Repository
為了處理User和Role之間的多對多關係,我們需要創建一個新的倉庫風格:
@Component
public class UserToRoleRelationshipRepository implements RelationshipRepositoryV2<User, Long, Role, Long> {
@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<Long> roleIds, String fieldName) {
Set<Role> roles = new HashSet<Role>();
roles.addAll(roleRepository.findAllById(roleIds));
user.setRoles(roles);
userRepository.save(user);
}
@Override
public void addRelations(User user, Iterable<Long> roleIds, String fieldName) {
Set<Role> roles = user.getRoles();
roles.addAll(roleRepository.findAllById(roleIds));
user.setRoles(roles);
userRepository.save(user);
}
@Override
public void removeRelations(User user, Iterable<Long> roleIds, String fieldName) {
Set<Role> 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<Role> findManyTargets(Long sourceId, String fieldName, QuerySpec querySpec) {
final Optional<User> userOptional = userRepository.findById(sourceId);
User user = userOptional.isPresent() ? userOptional.get() : new User();
return querySpec.apply(user.getRoles());
}
@Override
public Class<User> getSourceResourceClass() {
return User.class;
}
@Override
public Class<Role> getTargetResourceClass() {
return Role.class;
}
}
我們忽略了這裏的一些單方法,在關係倉庫中。
7. 測試
最後,讓我們分析一些請求並真正瞭解 JSON-API 輸出是什麼樣的。
我們將開始檢索單個用户資源(id = 2):
GET 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.relationships
- 由於我們使用了 @JsonApiRelation(serialize=SerializeType.EAGER) 對 roles 關係進行了配置,因此該關係包含在 JSON 中,並且位於節點 included
接下來,讓我們獲取包含角色的集合資源:
GET 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 是一項絕佳規範——它終於為我們API中的JSON添加了結構,並真正實現了超媒體API。
本文探討了一種在Spring應用中設置它的方法。但無論這種實現方式如何,我認為該規範本身非常具有前景。
為了處理
@Component
public class UserToRoleRelationshipRepository implements RelationshipRepositoryV2<User, Long, Role, Long> {
@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<Long> roleIds, String fieldName) {
Set<Role> roles = new HashSet<Role>();
roles.addAll(roleRepository.findAllById(roleIds));
user.setRoles(roles);
userRepository.save(user);
}
@Override
public void addRelations(User user, Iterable<Long> roleIds, String fieldName) {
Set<Role> roles = user.getRoles();
roles.addAll(roleRepository.findAllById(roleIds));
user.setRoles(roles);
userRepository.save(user);
}
@Override
public void removeRelations(User user, Iterable<Long> roleIds, String fieldName) {
Set<Role> 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<Role> findManyTargets(Long sourceId, String fieldName, QuerySpec querySpec) {
final Optional<User> userOptional = userRepository.findById(sourceId);
User user = userOptional.isPresent() ? userOptional.get() : new User();
return querySpec.apply(user.getRoles());
}
@Override
public Class<User> getSourceResourceClass() {
return User.class;
}
@Override
public Class<Role> getTargetResourceClass() {
return Role.class;
}
}
我們忽略了這裏的一些單方法,在關係倉庫中。
7. 測試
最後,讓我們分析一些請求並真正瞭解 JSON-API 輸出是什麼樣的。
我們將開始檢索單個用户資源(id = 2):
GET 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.relationships
- 由於我們使用了 @JsonApiRelation(serialize=SerializeType.EAGER) 對 roles 關係進行了配置,因此該關係包含在 JSON 中,並且位於節點 included
接下來,讓我們獲取包含角色的集合資源:
GET 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 是一項絕佳規範——它終於為我們API中的JSON添加了結構,並真正實現了超媒體API。
本文探討了一種在Spring應用中設置它的方法。但無論這種實現方式如何,我認為該規範本身非常具有前景。