J'ai donc choisi d'écrire une application Spring Boot et d'utiliser le plugin Spring Data Rest. Ce plugin scrupte tous vos objets qui étendent l'interface CrudRepository et les expose automatiquement via une API REST HATEOAS.
HATEOAS, pour ceux qui ne connaissent pas, impose quelques règles lorsque vous faites une application REST. Le sigle signifie " Hypermedia as the Engine of Application State". Le principe est de fournir tous les élements dans l'API pour que le client puisse interagir avec vos données. Pour résumer votre API envoie en plus des données, des éléments qui permettent de naviguer vers les différentes données de l'application.
Le mieux pour comprendre c'est de prendre un exemple. Nous allons donc voir comment écrire une application qui permet d'exposer une liste de talks et les speakers associés en quelques lignes. Tout le code montré dans cet article est disponible sous Github.
Générer un projet Spring Boot
Si vous disposez de la dernière version de IntelliJ vous pouvez le faire via les wizards. Sinon rendez vous sur la page https://start.spring.io/. Indiquez les données permettant d'identifier votre projet, puis sélectionnez les modules qui vont nous intéresser ici
- Rest respositories (va importer Spring Data Rest et vous n'avez pas non plus à sélectionner HATEOAS)
- JPA
- Base de données H2 afin de stocker nos données dans une base en mémoire
Configuration du projet
Vous pouvez maintenant ouvrir le projet dans votre IDE préféré. Le descripteur du projet (maven dans mon exemple) doit vous montrer les dépendances
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-rest</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency>
Vous devez également disposer d'une classe qui permet de lancer votre projet
@EnableJpaRepositories @SpringBootApplication public class AngularAndTestsApplication { public static void main(String[] args) { SpringApplication.run(AngularAndTestsApplication.class, args); } }
Testez que votre application fonctionne en lançant la commande
mvn spring-booot run
Elle est disponible à l'adresse http://localhost:8080
Pour le moment votre application n'expose qu'un service qui expose le numéro de version
Création de vos entités
Nous allons créer un objet Talk qui peut contenir une liste de speakers (je passe les détails des objets entités qui n'apportent rien comme certaines propriétés, les getters/setters et autres méthodes utilitaires)
@Entity public class Talk { @GeneratedValue @Id private Long id; private String title; @Column(columnDefinition = "TEXT") private String description; @OneToMany(targetEntity=Speaker.class, mappedBy="talk") private List<Speaker> speakers = new ArrayList<>(); ... }
Puis un objet Speaker qui contiendra une relation ManyToOne vers un Talk
@Entity public class Speaker { @GeneratedValue @Id private Long id; private String firstname; private String lastname; @ManyToOne private Talk talk; ...
}
Configuration du projet
Par défaut les services REST n'exposent pas les identifiants dans le JSON envoyé au client. Pour pouvoir les ajouter vous pouvez rajouter une classe de configuration héritant de la classe SpringBootRepositoryRestMvcConfiguration et dans laquelle nous allons déclarer les beans pour lesquels nous souhaitons exposer les id.
@Configuration public class AngularAndTestsConfig extends SpringBootRepositoryRestMvcConfiguration{ @Override protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) { config.exposeIdsFor(Speaker.class); config.exposeIdsFor(Talk.class); } }
server.contextPath=/api
Définition de nos repository
Nous arrivons bientôt au terme de notre projet. Nous définissons deux répositories qui ne sont que des interfaces. Spring s'occupe de générer les classes concrètes en fonction des noms des méthodes. Il suffit de respecter la convention exposée ici.
public interface SpeakerRepository extends CrudRepository<Speaker, Long> { Collection<Speaker> findByFirstname(@Param("name") String firstname); }
public interface TalkRepository extends CrudRepository<Talk, Long> { }
Test de l'application
Nous pouvons vérifier que notre application fonctionne en écrivant un test unitaire
@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = AngularAndTestsApplication.class) public class SpeakerRepositoryTest { @Autowired private SpeakerRepository repository; @Test public void should_find_speaker_by_firstname() throws Exception { repository.save(new Speaker().setFirstname("Dan").setLastname("North")); assertThat(repository.findByFirstname("Dan").iterator().next().getLastname()).isEqualTo("North"); } }
Nous pouvons également écrire une classe qui initialise une donnée dans la base
@Component public class Initializer { @Autowired private SpeakerRepository speakerRepository; @Autowired private TalkRepository talkRepository; @PostConstruct public void init() { speakerRepository.save( new Speaker() .setFirstname("Guillaume") .setLastname("EHRET") .setTalk(talkRepository.save( new Talk()
.setTitle("Un exemple avec Spring Data Rest") ))); } }
Si la syntaxe de création d'une donnée vous interpelle je vous conseille de lire mon article sur l'écriture des DTO en Java.
Vous pouvez lancer l'application via l'URL http://localhost:8080/api.
Vous pouvez naviguer directement vers vos données
Conclusion
Personnellement j'ai vraiment été séduit par la rapidité à laquelle j'ai pu créer une application Restfull. Au départ, j'ai eu un peu de mal pour manipuler l'API REST mais tout ceci est lié à ma méconnaissance des bonnes pratiques à ce sujet. Lire la documentation HATEOAS m'a donné plusieurs élements de réponse.
Au niveau des objets nous ne définissons plus des objets imbriqués les uns dans les autres mais nous utilisons des aLink. Par exemple si je veux sauvegarder un speaker j'enverrai un JSON de la forme suivante
Pour une création j'envoie cet objet via une méthode POST à l'URL /speakers
Pour une modification je l'envoie via une méthode PUT à l'URL /speakers/1 (ne pas oublier l'ID)
Voila je vous laisse découvrir par vous même Spring Data Rest et les sources de ma démo sont disponibles sous Github.
Aucun commentaire:
Enregistrer un commentaire
Remarque : Seul un membre de ce blog est autorisé à enregistrer un commentaire.