API JSON dans une application Spring
1. Vue d'ensemble
Dans cet article, nous allons commencer à explorerthe JSON-API spec et comment l'intégrer dans une API REST Spring.
Nous allons utiliser l'implémentationKatharsis de JSON-API en Java - et nous allons configurer une application Spring basée sur Katharsis - donc tout ce dont nous avons besoin est une application Spring.
2. Maven
Jetons d'abord un coup d'œil à notre configuration maven - nous devons ajouter la dépendance suivante dans nospom.xml:
io.katharsis
katharsis-spring
3.0.2
3. Une ressource utilisateur
Jetons ensuite un œil à notre ressource utilisateur:
@JsonApiResource(type = "users")
public class User {
@JsonApiId
private Long id;
private String name;
private String email;
}
Notez que:
-
L'annotation@JsonApiResource est utilisée pour définir notre ressourceUser
-
L'annotation@JsonApiId est utilisée pour définir l'identifiant de la ressource
Et très brièvement - la persistance de cet exemple va être un référentiel Spring Data ici:
public interface UserRepository extends JpaRepository {}
4. Un référentiel de ressources
Ensuite, parlons de notre référentiel de ressources - chaque ressource doit avoir unResourceRepositoryV2 pour publier les opérations d'API disponibles dessus:
@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);
}
}
Une note rapide ici - c'est bien sûrvery similar to a Spring controller.
5. Configuration de Katharsis
Comme nous utilisonskatharsis-spring, tout ce que nous devons faire est d'importerKatharsisConfigV3 dans notre application Spring Boot:
@Import(KatharsisConfigV3.class)
Et configurez les paramètres Katharsis dans nosapplication.properties:
katharsis.domainName=http://localhost:8080
katharsis.pathPrefix=/
Avec cela - nous pouvons maintenant commencer à consommer l'API; par exemple:
-
GET «http://localhost:8080/users»: pour obtenir tous les utilisateurs.
-
POST «http://localhost:8080/users»: pour ajouter un nouvel utilisateur, et plus encore.
6. Des relations
Voyons ensuite comment gérer les relations d'entités dans notre API JSON.
6.1. Ressource de rôle
Tout d'abord, introduisons une nouvelle ressource -Role:
@JsonApiResource(type = "roles")
public class Role {
@JsonApiId
private Long id;
private String name;
@JsonApiRelation
private Set users;
}
Et puis configurez une relation plusieurs-à-plusieurs entreUser etRole:
@JsonApiRelation(serialize=SerializeType.EAGER)
private Set roles;
6.2. Référentiel de ressources de rôle
Très rapidement - voici notre référentiel de ressourcesRole:
@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);
}
}
Il est important de comprendre ici que ce référentiel de ressources unique ne gère pas l'aspect relation - qui prend un référentiel distinct.
6.3. Référentiel de relations
Afin de gérer la relation plusieurs-à-plusieurs entreUser -Role, nous devons créer un nouveau style de référentiel:
@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;
}
}
Nous ignorons les méthodes singulières ici, dans le référentiel de relations.
7. Test
Enfin, analysons quelques requêtes et comprenons vraiment à quoi ressemble la sortie de l'API JSON.
Nous allons commencer à récupérer une seule ressource utilisateur (avec 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"
}
}
]
}
À emporter:
-
Les principaux attributs de la ressource se trouvent dansdata.attributes
-
Les principales relations de la ressource se trouvent dansdata.relationships
-
Comme nous avons utilisé@JsonApiRelation(serialize=SerializeType.EAGER) pour la relationroles, il est inclus dans le JSON et trouvé dans le nœudincluded
Ensuite, récupérons la ressource de collection contenant les rôles:
{
"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":[
]
}
La conclusion rapide ici est que nous obtenons tous les rôles du système - sous forme de tableau dans le nœuddata
8. Conclusion
JSON-API est une spécification fantastique - il nous faut enfin ajouter une structure à la manière dont nous utilisons JSON dans nos API et créer une véritable API Hypermedia.
Cet article explore une façon de le configurer dans une application Spring. Mais quelle que soit cette mise en œuvre, la spécification elle-même est - à mon avis - un travail très très prometteur.
Le code source complet de l'exemple est disponible suron GitHub. C'est un projet Maven qui peut être importé et exécuté tel quel.