Tutoriel sur la création de Web services RESTful avec Spring

Abdelhamid KADI

Cet article présente les Web services RESTful avec Spring (création, configuration, tests et déploiement).

Pour réagir au contenu de cet article, un espace de dialogue vous est proposé sur le forum. Commentez .

- Titre : Tutoriel sur la création de Web services RESTful avec Spring

- Auteur : Abdelhamid KADI

- Parution : 26 octobre 2015

- Licence : Copyright ® 2015 Abdelhamid KADI. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- I-A. Objectif

- I-B. Prérequis

- I-C. Versions des logiciels

- I-D. Création du projet dans Eclipse

- II. Configuration du fichier Maven pom.xml

- II-A. Définition des versions des api/framework

- II-B. Configurer les plugins

- II-C. Dépendances pour les tests unitaires

- II-D. Dépendances Spring

- II-E. Autres Dépendances

- II-F. Mise à jour du projet Maven

- III. Configuration de la couche DAO

- III-A. Configuration du contexte Spring

- III-B. Configuration de la source de données

- III-C. Développement des couches DAO

- IV. Tests et implémentation des méthodes de la couche DAO

- IV-A. Test de la couche DAO

- IV-B. Implémentation des méthodes de la couche DAO

- V. Configuration de la couche MVC

- V-A. Configuration de Spring MVC

- V-B. Développement de la couche Contrôleur

- V-C. Tests unitaires des méthodes de contrôleur

- VI. Déploiement du Web service

- VI-A. Installation de Tomcat Sous Eclipse

- VI-B. Installation d'un client REST pour Chrome

- VII. Conclusion et remerciements

I - Introduction

Les services Web sont un moyen rapide de distribution de l'information entre les clients et les fournisseurs. Il existe deux grands types de services Web :

- les services Web de type REST (REpresentational State Transfer) qui exposent leurs fonctionnalités en protocole HTTP ou URI ;

- les services Web qui exposent leurs fonctionnalités en protocoles SOAP et WSDL.

Les services Web permettent la communication et l'échange de données entre des systèmes et applications utilisant des différentes technologies.

Le support du protocole REST a été introduit dans le module Spring-MVC à partir de la version 3 de Spring. Les Web services REST utilisent quatre types de méthodes http GET, POST, PUT et DELETE.

Pour mieux appréhender REST, il est vivement conseillé de lire la page suivante : http://www.restapitutorial.com/lessons/restquicktips.html.

I-A - Objectif

Tout au long de cet article, j'expliquerai étape par étape, comment implémenter un simple Web service REST avec le framework Spring 4.
Les principales étapes sont :

- créer un projet Maven dans Eclipse ;

- configurer le fichier pom.xml ;

- configurer Spring pour la couche [DAO] ;

- développer les méthodes de la couche [DAO] ;

- configurer Spring pour la couche MVC ;

- exposer les méthodes du Web service ;

- sécuriser les méthodes du Web service (partie 2 du tutoriel).

I-B - Prérequis

Il est important d'avoir des notions sur Java/J2EE et sur les principaux modules du framework Spring, à savoir :

- Spring core ;

- Spring MVC ;

- Spring Security ;

- Spring JDBC.

I-C - Versions des logiciels

Les versions des principaux logiciels et frameworks utilisés dans cet article sont :

- Eclipse Luna ;

- JDK 8 ;

- Tomcat 8.0 ;

- Maven 3 ;

- Spring 4.1.4 ;

- HSQLDB 2.3.2.

I-D - Création du projet dans Eclipse

Pour commencer, nous allons créer une application Web dynamique à partir de Eclipse en utilisant le plugin Maven de Eclipse.

Clic droit dans l'onglet Project Explorer, choisir New puis Other :

La fenêtre suivante s'affiche :

Dans le champ Wizards, saisir Maven, sélectionner Maven Project, puis cliquer sur le bouton Next :

Cocher la case « create a simple… » puis Next :
Saisir les valeurs suivantes :
Groupe Id : fr.idak.tutorial.ws ;
Artifact Id : rest-ws ;
Packaging : war.

Cliquer sur le bouton Finish pour finaliser la création du projet. La structure du projet généré est la suivante :

Un projet Maven standard se compose des dossiers suivants :

- src/main/java : contient les sources Java ;

- src/main/java : contient toutes les sources Java pour les tests unitaires ;

- src/main/resources : contient les fichiers de configuration ;

- target : contient les livrables et les résultats des tests unitaires ;

- pom.xml : contient toute la configuration Maven du projet, à savoir : dépendances, plugins, paramètres…

II - Configuration du fichier Maven pom.xml

II-A - Définition des versions des api/framework

Les numéros des versions des api/framework utilisées sont définis entre les balises <properties> et </properties> :

1. <properties>

2. <jdk.version>1.8</jdk.version>

3. <junit.version>4.12</junit.version>

4. <hamcrest.version>1.3</hamcrest.version>

5. <json.path.version>0.8.1</json.path.version>

6. <mockito.version>1.9.5</mockito.version>

7. <slf4j.version>1.7.9</slf4j.version>

8. <hsqldb.version>1.8.0.10</hsqldb.version>

9. <spring.version>4.1.3.RELEASE</spring.version>

10. <servlet.api.version>3.1.0</servlet.api.version>

11. <jackson.version>2.4.4</jackson.version>

12. </properties>

II-B - Configurer les plugins

Les plugins sont définis entre les balises <build> et </build> :

1. <build>

2. <finalName>rest-ws</finalName>

3. <plugins>

4. <!-- Configuration du plugin de compilation -->

5. <plugin>

6. <groupId>org.apache.maven.plugins</groupId>

7. <artifactId>maven-compiler-plugin</artifactId>

8. <version>3.2</version>

9. <configuration>

10. <source>${jdk.version}</source>

11. <target>${jdk.version}</target>

12. </configuration>

13. </plugin>

14. <!-- Configuration du plugin de génération du war -->

15. <plugin>

16. <groupId>org.apache.maven.plugins</groupId>

17. <artifactId>maven-war-plugin</artifactId>

18. <configuration>

19. <failOnMissingWebXml>false</failOnMissingWebXml>

20. </configuration>

21. </plugin>

22. </plugins>

23. </build>

Le fichier de déploiement web.xml n'est plus obligatoire dans la version 3.0 de la Servlet. L'élément failOnMissingWebXml permet d'indiquer à Maven que le fichier web.xml n'est pas utilisé.

II-C - Dépendances pour les tests unitaires

Nous allons utiliser la méthode TDD (Test Driven Development) pour implémenter les méthodes du Web service, pour cela nous avons besoin des API de test suivantes :

Junit 4

1. <dependency>

2. <groupId>junit</groupId>

3. <artifactId>junit</artifactId>

4. <version>${junit.version}</version>

5. <scope>test</scope>

6. <exclusions>

7. <exclusion>

8. <artifactId>hamcrest-core</artifactId>

9. <groupId>org.hamcrest</groupId>

10. </exclusion>

11. </exclusions>

12. </dependency>

Jsonpath : pour tester les flux JSON du Web service

1. <dependency>

2. <groupId>com.jayway.jsonpath</groupId>

3. <artifactId>json-path</artifactId>

4. <version>${json.path.version}</version>

5. <scope>test</scope>

6. </dependency>

7. <dependency>

8. <groupId>com.jayway.jsonpath</groupId>

9. <artifactId>json-path-assert</artifactId>

10. <version>${json.path.version}</version>

11. <scope>test</scope>

12. </dependency>

Hamcrest : pour assertion des résultats des tests

1. <dependency>

2. <groupId>org.hamcrest</groupId>

3. <artifactId>hamcrest-all</artifactId>

4. <version>${hamcrest.version}</version>

5. <scope>test</scope>

6. </dependency>

Spring-test : pour initialiser le contexte Spring pour pouvoir utiliser l'injection des dépendances

1. <dependency>

2. <groupId>org.springframework</groupId>

3. <artifactId>spring-test</artifactId>

4. <version>${spring.version}</version>

5. <scope>test</scope>

6. </dependency>

II-D - Dépendances Spring

Dans la première partie de cet article, nous allons utiliser deux modules de Spring, à savoir :

Spring-mvc et Spring-jdbc

1. <dependency>

2. <groupId>org.springframework</groupId>

3. <artifactId>spring-webmvc</artifactId>

4. <version>${spring.version}</version>

5. </dependency>

6. <dependency>

7. <groupId>org.springframework</groupId>

8. <artifactId>spring-jdbc</artifactId>

9. <version>${spring.version}</version>

10. </dependency>

II-E - Autres Dépendances

La configuration pour le logeur, la Servlet, la base HSQLDB et Jackson mapper est :

1. <!-- Dépendances du Logeur -->

2. <dependency>

3. <groupId>org.slf4j</groupId>

4. <artifactId>slf4j-log4j12</artifactId>

5. <version>${slf4j.version}</version>

6. </dependency>

8. <!-- Dépendances de la Servlet Api -->

9. <dependency>

10. <groupId>javax.servlet</groupId>

11. <artifactId>javax.servlet-api</artifactId>

12. <version>${servlet.api.version}</version>

13. </dependency>

15. <!-- Dépendances HSQLDB -->

16. <dependency>

17. <groupId>hsqldb</groupId>

18. <artifactId>hsqldb</artifactId>

19. <version>${hsqldb.version}</version>

20. </dependency>

22. <!-- dépendances pour jackson mapper -->

23. <dependency>

24. <groupId>com.fasterxml.jackson.core</groupId>

25. <artifactId>jackson-core</artifactId>

26. <version>${jackson.version}</version>

27. </dependency>

29. <dependency>

30. <groupId>com.fasterxml.jackson.core</groupId>

31. <artifactId>jackson-databind</artifactId>

32. <version>${jackson.version}</version>

33. </dependency>

34. <dependency>

35. <groupId>com.fasterxml.jackson.core</groupId>

36. <artifactId>jackson-annotations</artifactId>

37. <version>${jackson.version}</version>

38. </dependency>

II-F - Mise à jour du projet Maven

Clic droit sur le projet puis choisir le sous-menu Maven -> Update Project ; puis OK :

Il reste deux problèmes à corriger, à savoir la version de Java et de la Servlet :

Pour régler ces deux problèmes, il suffirait de modifier le fichier org.eclipse.wst.common.project.facet.core.xml du répertoire .seings, le répertoire de trouve à la racine du projet Eclipse.

1. <?xml version="1.0" encoding="UTF-8"?>

2. <faceted-project>

3. <fixed facet="wst.jsdt.web"/>

4. <installed facet="java" version="1.8"/>

5. <installed facet="jst.web" version="3.0"/>

6. <installed facet="wst.jsdt.web" version="1.0"/>

7. </faceted-project>

Puis mettre à jour le projet Maven -> Update Project.

III - Configuration de la couche DAO

L'architecture du notre Web service évolue comme suit :

III-A - Configuration du contexte Spring

La structure du projet pour les prochaines étapes est :

Créer la classe ContextConfig qui nous permettra de configurer le contexte Spring du Web service.
Annoter la classe par @Configurion pour indiquer à Spring que cette classe peut être utilisée par le conteneur Spring IOC comme une source de définition des Beans.
Les Beans de notre repository seront ajoutés au contexte par l'annotation @ComponentScan.

1. package fr.idak.tutorial.ws.config;

3. import org.springframework.context.annotation.ComponentScan;

4. import org.springframework.context.annotation.Configuration;

6. @Configuration

7. @ComponentScan(value = {"fr.idak.tutorial.ws.repository"})

8. public class ContextConfig {

10. }

III-B - Configuration de la source de données

Ajouter à la classe ContextConfig la méthode dataSource qui nous permettra d'initialiser la dataSource et de créer une base de données en mémoire. La communication avec la base de données sera assurée par la classe Spring JdbcTemplate définie par la méthode jdbcTemplate.
L'annotation @Bean retourne un objet qui devrait être ajouté au contexte Spring de l'application.

1. package fr.idak.tutorial.ws.config;

3. import javax.sql.DataSource;

5. import org.springframework.context.annotation.Bean;

6. import org.springframework.context.annotation.ComponentScan;

7. import org.springframework.context.annotation.Configuration;

8. import org.springframework.jdbc.core.JdbcTemplate;

9. import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;

11. @Configuration

12. @ComponentScan(value = {"fr.idak.tutorial.ws.repository"})

13. public class ContextConfig {

15. @Bean

16. public DataSource dataSource(){

17. EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();

18. builder.addScript("db/schema.sql");

19. builder.addScript("db/data.sql");

20. return  builder.build();

21. }

23. @Bean

24. public JdbcTemplate jdbcTemplate(){

25. return new JdbcTemplate(dataSource());

26. }

28. }

La méthode dataSource utilise deux fichiers SQL, le premier pour créer le schéma de la base de données et le second pour insérer quelques données de test. Pour simplifier l'exemple, ces fichiers contiendront une seule table nommée BOOK qui sera utilisée tout au long de cet article.

schema.sql

drop table BOOK if exists;
create table BOOK (
BOOK_ID bigint generated by default as identity (start with 1),
TITLE     varchar(145),
ISBN      varchar(13),
PRICE     decimal(10,2),
DT_CREATE date default CURRENT_DATE,
DT_UPDATE date,
primary key (BOOK_ID)
);

data.sql

insert into BOOK (TITLE, ISBN,PRICE, DT_UPDATE)
values ('Spring par la Pratique Spring 2.5 et 3.0', '9782212124217', '34.99 ','2015-01-02');
insert into BOOK (TITLE, ISBN,PRICE)
values ('Programmer en Java : Couvre les nouveautés de Java 8, streams, expressions lambda', '9782212140071', '36.00');
insert into BOOK (TITLE, ISBN,PRICE)
values ('Java Pour les Nuls, Nouvelle édition', '9782754055888','15.99');
insert into BOOK (TITLE, ISBN,PRICE)
values ('Pro Spring MVC: With Web Flow (Professional Apress)', '9781430241553', '51.05');
insert into BOOK (TITLE, ISBN,PRICE)
values ('Spring in Action', '9781617291203', '42.40');
insert into BOOK (TITLE, ISBN,PRICE)
values ('AngularJS - Développez aujourd''hui les applications web de demain', '9782746093348', '54.00');
insert into BOOK (TITLE, ISBN,PRICE)
values ('Pro AngularJS', '9781430264484','35.82');
insert into BOOK (TITLE, ISBN,PRICE)
values ('Node.js, MongoDB, and AngularJS Web Development', '9780321995780', '43.33');
insert into BOOK (TITLE, ISBN,PRICE)
values ('Mastering KnockoutJS', '9781783981007','42.18');
insert into BOOK (TITLE, ISBN,PRICE)
values ('Java Persistence with Hibernate', '9781617290459', '51.28');
insert into BOOK (TITLE, ISBN,PRICE)
values ('Java Persistence et Hibernate', '9782212122596', '81.00');

III-C - Développement des couches DAO

Créer la classe Java pour le modèle Book, cette classe est une représentation de la table BOOK :

1. package fr.idak.tutorial.ws.model;

3. import java.math.BigDecimal;

4. import java.util.Date;

6. public class Book {

7. private Long bookId;

8. private String isbn;

9. private String title;

10. private BigDecimal price;

11. private Date dtCreate;

12. private Date dtUpdate;

13. // getter et setter ...

14. }

Créer l'interface BookRepository :

1. package fr.idak.tutorial.ws.repository;

3. import java.util.List;

4. import fr.idak.tutorial.ws.model.Book;

6. public interface BookRepository {

7. /**

8. * Permet de récupérer tous les enregistrements de la table BOOK

9. * @return Liste de Book

10. */

11. List<Book> getAll();

13. /**

14. * Permet de rechercher un BOOK à partir de son identifiant

15. * @param id

16. * @return Book

17. */

18. Book get(Long id);

20. /**

21. * Permet d'ajouter un nouvel enregistrement de la table BOOK

22. * @param book

23. */

24. void create(Book book);

26. /**

27. *

28. * Permet de supprimer un BOOK

29. * @param book

30. */

31. void delete(Book book);

33. /**

34. * Permet de mettre à jour un BOOK

35. * @param book

36. */

37. void update(Book book);

39. }

Créer une nouvelle classe BookRepositoryImpl qui implémente l'interface BookRepository. Annoter la classe par @Repository pour indiquer à Spring que la classe doit être ajoutée au contexte comme étant un Bean de type repository.

L'annotation @Resource permet d'injecter une référence du Bean JdbcTemplate.

1. package fr.idak.tutorial.ws.repository;

3. import java.util.List;

4. import javax.annotation.Resource;

5. import org.springframework.jdbc.core.JdbcTemplate;

6. import org.springframework.stereotype.Repository;

7. import fr.idak.tutorial.ws.model.Book;

9. @Repository

10. public class BookRepositoryImpl implements BookRepository {

12. @Resource

13. private JdbcTemplate jt;

15. @Override

16. public Book get(Long id) {

17. // TODO Auto-generated method stub

18. return null;

19. }

21. @Override

22. public List<Book> getAll() {

23. // TODO Auto-generated method stub

24. return null;

25. }

28. @Override

29. public void create(Book article) {

30. // TODO Auto-generated method stub

31. }

33. @Override

34. public void delete(Book article) {

35. // TODO Auto-generated method stub

36. }

38. @Override

39. public void update(Book article) {

40. // TODO Auto-generated method stub

41. }

42. }

IV - Tests et implémentation des méthodes de la couche DAO

Les méthodes de la classe BookRepository ne sont pas encore implémentées, mais cela ne nous empêchera pas de tester les méthodes du Web service et de continuer à les implémenter en utilisant la technique TDD.

Pour mieux comprendre la méthode TDD, il est vivement conseillé de lire la page suivante : http://fr.wikipedia.org/wiki/Test_Driven_Development.

IV-A - Test de la couche DAO

Nous allons créer une nouvelle classe BookRepositoryTest qui nous permettra de tester la configuration Spring et les méthodes du la couche DAO :

- @RunWith : indique à Junit que les tests unitaires seront exécutés par la classe Spring SpringJUnit4ClassRunner ;

- @ContextConfiguration : permet de charger le contexte Spring défini dans la classe ContextConfig.

1. package fr.idak.tutorial.ws.repository;

3. import java.util.List;

4. import javax.annotation.Resource;

5. import org.junit.Assert;

6. import org.junit.Test;

7. import static org.hamcrest.CoreMatchers.*;

8. import org.junit.runner.RunWith;

9. import org.springframework.test.context.ContextConfiguration;

10. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

11. import fr.idak.tutorial.ws.config.ContextConfig;

12. import fr.idak.tutorial.ws.model.Book;

13. import fr.idak.tutorial.ws.repository.BookRepository;

15. @RunWith(SpringJUnit4ClassRunner.class)

16. @ContextConfiguration(classes={ContextConfig.class})

17. public class BookRepositoryTest {

19. @Resource

20. private BookRepository bookRepository;

22. @Test

23. public void getBook(){

24. Long id = 1L;

25. Book books = bookRepository.get(id);

26. Assert.assertNotNull(books);

27. Assert.assertThat(id, is(books.getBookId()));

28. }

30. @Test

31. public void readAll(){

32. List<Book> books = bookRepository.getAll();

33. Assert.assertNotNull(books);

34. Assert.assertTrue(books.size() > 0);

35. }

36. }

L'exécution de ces tests unitaires nous indique qu'ils échouent, il va falloir implémenter les méthodes de la classe BookRepositoryImpl afin que les tests unitaires passent :

IV-B - Implémentation des méthodes de la couche DAO

Nous allons utiliser la classe JdbcTemplate et BeanPropertyRowMapper du module Spring-jdbc de Spring pour communiquer avec la base de données.
BeanPropertyRowMapper : permet d'extraire les données de ResultSet sans implémenter un RowMapper. Pour utiliser ce Bean, les noms des attributs du modèle Book et des colonnes de la table BOOK doivent être cohérents, voire identiques (BOOK_ID -> bookId, DT_CREATE -> dtCreate).

L'implémentation des méthodes get(id) et getAll() de la classe BookRepositoryImpl est :

1. @Override

2. public Book get(Long id) {

3. String GET_ARTICLE_BY_ID = "select * from book where book_id=?";

4. return  jt.queryForObject(GET_ARTICLE_BY_ID, BeanPropertyRowMapper.newInstance(Book.class), id) ;

5. }

7. @Override

8. public List<Book> getAll() {

9. String GET_ALL_ARTICLES = "select * from book";

10. return jt.query(GET_ALL_ARTICLES, BeanPropertyRowMapper.newInstance(Book.class));

11. }

Désormais, les tests unitaires passent, donc on peut passer à l'étape suivante.

Les autres méthodes de la DAO peuvent être implémentées de la même façon.

V - Configuration de la couche MVC

V-A - Configuration de Spring MVC

La structure du projet pour les prochaines étapes est la suivante :

Pour configurer la couche Spring MVC, nous avons besoin de créer deux nouvelles classes :

WebMvcConfig : permet de configurer le contexte web MVC du Web service.

1. package fr.idak.tutorial.ws.config;

3. import org.springframework.context.annotation.ComponentScan;

4. import org.springframework.context.annotation.Configuration;

5. import org.springframework.web.servlet.config.annotation.EnableWebMvc;

6. import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

8. @EnableWebMvc

9. @Configuration

10. @ComponentScan(basePackages={"fr.idak.tutorial.ws.web"})

11. public class WebMvcConfig  extends WebMvcConfigurerAdapter{

13. }

WebAppInitialiser : joue le rôle du fichier de déploiement web.xml standard.

1. package fr.idak.tutorial.ws.config;

3. import javax.servlet.ServletContext;

4. import javax.servlet.ServletException;

5. import javax.servlet.ServletRegistration;

6. import org.springframework.web.WebApplicationInitializer;

7. import org.springframework.web.context.ContextLoaderListener;

8. import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;

9. import org.springframework.web.servlet.DispatcherServlet;

11. public class WebAppInitializer implements WebApplicationInitializer{

13. @Override

14. public void onStartup(ServletContext servletContext) throws ServletException {

15. AnnotationConfigWebApplicationContext rootContext =

16. new AnnotationConfigWebApplicationContext();

18. //Déclaration de la classe de configuration

19. //du contexte (dataSource, repositories, spring-security …)

20. rootContext.register(ContextConfig.class);

21. servletContext.addListener(new ContextLoaderListener(rootContext));

23. //Création du distapcher de servlet

24. AnnotationConfigWebApplicationContext dispatcherServlet =

25. new AnnotationConfigWebApplicationContext();

26. dispatcherServlet.register(WebMvcConfig.class);

28. //Déclaration du distapcher de servlet

29. ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher",

30. new DispatcherServlet(dispatcherServlet));

31. dispatcher.setLoadOnStartup(1);

32. dispatcher.addMapping("/");

33. }

34. }

V-B - Développement de la couche Contrôleur

Dans cette partie, nous allons exposer une série de méthodes REST standard qui permettent à des applications clientes de réaliser des opérations (CRUD) sur des bouquins. Ces méthodes seront implémentées dans une nouvelle classe BookController.

Désormais avec la version 4 de Spring, il n'y a plus besoin d'effectuer la conversion Objet en JSON et inversement. Le marshaling et le unmarshaling sont effectués grâce à l'annotation @RestController.

@RequestMapping permet d'indiquer le mapping d'URL pour lequel le contrôleur ou la méthode doit répondre :

- value : définit l'URL à traiter ;

- method : définit le type de la requête, GET dans ce cas.

@PathVariable permet de spécifier une valeur en dehors de l'URL (ex. : id) et de l'assigner aux paramètres de la méthode.

1. package fr.idak.tutorial.ws.web.controller;

3. import java.util.List;

4. import javax.annotation.Resource;

5. import org.springframework.web.bind.annotation.PathVariable;

6. import org.springframework.web.bind.annotation.RequestMapping;

7. import org.springframework.web.bind.annotation.RequestMethod;

8. import org.springframework.web.bind.annotation.RestController;

9. import fr.idak.tutorial.ws.model.Book;

10. import fr.idak.tutorial.ws.repository.BookRepository;

12. @RestController

13. @RequestMapping(value="/api")

14. public class BookController {

16. @Resource

17. private BookRepository bookRepository;

19. @RequestMapping(value="/book/{id}", method=RequestMethod.GET)

20. public Book getBook(@PathVariable Long id){

21. return bookRepository.get(id);

22. }

24. @RequestMapping(value="/books", method=RequestMethod.GET)

25. public List<Book> getBooks(){

26. return bookRepository.getAll();

27. }

28. }

V-C - Tests unitaires des méthodes de contrôleur

Nous allons créer une nouvelle classe de test BookControllerTest qui nous permettra de tester les méthodes du Contrôleur BookController.

@WebAppConfiguration : permet d'indiquer au contexte Spring que nous allons utiliser un WebApplicationContext lors d'exécution des tests. Cette annotation est nécessaire pour la configuration du Mock MVC de Spring. La classe MockMvc est initialisée dans la méthode init() à partir de la classe WebApplicationContext avant chaque test.

CheckGetBookByIdUrl() : vérifie le bon fonctionnement l'URL '/api/book/{id}' du Web service.

CheckGetBooksUrl() : vérifie le bon fonctionnement l'URL '/api/books' du Web service.

getBooks() : vérifie le nombre de bouquins par l'URL /api/books du Web service.

D'autres tests unitaires peuvent être ajoutés à la classe pour avoir une bonne couverture de code pour les méthodes du contrôleur. Pour approfondir vos connaissances en tests unitaires de la couche Web, je vous conseille de consulter la page suivante : http://spring.io/guides/tutorials/bookmarks/.

1. package fr.idak.tutorial.ws.web.controller;

3. import org.hamcrest.Matchers;

4. import org.junit.Before;

5. import org.junit.Test;

6. import org.junit.runner.RunWith;

7. import org.springframework.test.context.ContextConfiguration;

8. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

9. import org.springframework.test.context.web.WebAppConfiguration;

10. import org.springframework.test.web.servlet.MockMvc;

11. import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

12. import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

13. import org.springframework.test.web.servlet.setup.MockMvcBuilders;

14. import org.springframework.web.context.WebApplicationContext;

15. import fr.idak.tutorial.ws.config.ContextConfig;

16. import fr.idak.tutorial.ws.config.WebMvcConfig;

17. import fr.idak.tutorial.ws.repository.BookRepository;

19. @RunWith(SpringJUnit4ClassRunner.class)

20. @ContextConfiguration(classes = {ContextConfig.class, WebMvcConfig.class})

21. @WebAppConfiguration

22. public class BookControllerTest {

24. @Resource

25. private WebApplicationContext wac;

27. @Resource

28. private BookRepository bookRepositoryMock;

30. private MockMvc mockMvc;

32. @Before

33. public void init() {

34. this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();

35. }

37. @Test

38. public void checkGetBookByIdUrl() throws Exception{

39. mockMvc.perform(MockMvcRequestBuilders.get("/api/book/1"))

40. .andExpect(MockMvcResultMatchers.status().isOk());

42. }

43. @Test

44. public void checkGetBooksUrl() throws Exception{

45. mockMvc.perform(MockMvcRequestBuilders.get("/api/books"))

46. .andExpect(MockMvcResultMatchers.status().isOk());

47. }

49. @Test

50. public void getBooks() throws Exception{

51. mockMvc.perform(MockMvcRequestBuilders.get("/api/books"))

52. .andExpect(MockMvcResultMatchers.status().isOk())

53. .andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(11)));

54. }

55. }

VI - Déploiement du Web service

VI-A - Installation de Tomcat Sous Eclipse

Nous avons besoin d'installer un serveur Tomcat pour pouvoir tester le déploiement et le bon fonctionnement du Web service.

- Télécharger la version 8 de Tomcat à partir du site http://tomcat.apache.org/.

- Dézipper l'archive.

- Créer le serveur Tomcat dans Eclipse (voir http://blog.paumard.org/tutoriaux/eclipse-tomcat/).

- Ajouter le projet Web service au serveur : dans l'onglet Servers, Clic droit, puis Add and Remove…

Dans la fenêtre qui s'affichera, sélectionner le projet, puis cliquer sur le bouton Add pour l'ajouter au serveur puis sur Finish :

- Démarrer le serveur en cliquant sur la petite icône « play ».

- Dans votre navigateur, saisir http://localhost:8080/rest-ws/books.
Si la liste des books s'affiche à l'écran, la configuration du serveur est terminée.

VI-B - Installation d'un client REST pour Chrome

Il est conseillé d'utiliser un client REST pour tester les Web services REST, plusieurs plugins sont disponibles pour Firefox et Chrome, personnellement j'utilise le plugin Postman sous Chrome :

VII - Conclusion et remerciements

L'objectif initial de ce tutoriel était la sécurité des services Web, j'ai préféré commencer par une présentation d'un simple Web service qui sera le fil conducteur des prochains tutoriels, à savoir :

- sécurité du Web service ;

- utilisation du Web service dans webapp (Spring MVC + KnockoutJS).

Le projet rest-ws peut être importé à partir du l'URL github suivante : https://github.com/idak/idak-projects/tree/master/lambda.

Un grand merci à Mickael BARON et Régis POUILLER pour leur aide ainsi qu'à Claude LELOUP pour la relecture orthographique.