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

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 Donner une note à l'article (5).

Article lu   fois.

L'auteur

Profil ProSite personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

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.

Image non disponible

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 :

Image non disponible

La fenêtre suivante s'affiche :

Image non disponible

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

Image non disponible

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

Image non disponible

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

Image non disponible

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> :

 
Sélectionnez
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
<properties>
    <jdk.version>1.8</jdk.version>
    <junit.version>4.12</junit.version>
    <hamcrest.version>1.3</hamcrest.version>
    <json.path.version>0.8.1</json.path.version>
    <mockito.version>1.9.5</mockito.version>
    <slf4j.version>1.7.9</slf4j.version>
    <hsqldb.version>1.8.0.10</hsqldb.version>
    <spring.version>4.1.3.RELEASE</spring.version>
    <servlet.api.version>3.1.0</servlet.api.version>
    <jackson.version>2.4.4</jackson.version>
</properties>

II-B. Configurer les plugins

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

 
Sélectionnez
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
<build>
    <finalName>rest-ws</finalName>
    <plugins>
        <!-- Configuration du plugin de compilation -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.2</version>
            <configuration>
                <source>${jdk.version}</source>
                <target>${jdk.version}</target>
            </configuration>
        </plugin>
        <!-- Configuration du plugin de génération du war -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <configuration>
                <failOnMissingWebXml>false</failOnMissingWebXml>
            </configuration>
        </plugin>
    </plugins>
</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
Sélectionnez
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>${junit.version}</version>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <artifactId>hamcrest-core</artifactId>
            <groupId>org.hamcrest</groupId>
        </exclusion>
    </exclusions>
</dependency>
Jsonpath : pour tester les flux JSON du Web service
Sélectionnez
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
<dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
    <version>${json.path.version}</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path-assert</artifactId>
    <version>${json.path.version}</version>
    <scope>test</scope>
</dependency>
Hamcrest : pour assertion des résultats des tests
Sélectionnez
83.
84.
85.
86.
87.
88.
<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest-all</artifactId>
    <version>${hamcrest.version}</version>
    <scope>test</scope>
</dependency>
Spring-test : pour initialiser le contexte Spring pour pouvoir utiliser l'injection des dépendances
Sélectionnez
90.
91.
92.
93.
94.
95.
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>${spring.version}</version>
    <scope>test</scope>
</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
Sélectionnez
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>${spring.version}</version>
</dependency>

II-E. Autres Dépendances

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

 
Sélectionnez
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
<!-- Dépendances du Logeur -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>${slf4j.version}</version>
</dependency>

<!-- Dépendances de la Servlet Api -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>${servlet.api.version}</version>
</dependency>
        
<!-- Dépendances HSQLDB -->
<dependency>
    <groupId>hsqldb</groupId>
    <artifactId>hsqldb</artifactId>
    <version>${hsqldb.version}</version>
</dependency>

<!-- dépendances pour jackson mapper -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>${jackson.version}</version>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>${jackson.version}</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>${jackson.version}</version>
</dependency>

II-F. Mise à jour du projet Maven

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

Image non disponible

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

Image non disponible

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.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
<?xml version="1.0" encoding="UTF-8"?>
<faceted-project>
  <fixed facet="wst.jsdt.web"/>
  <installed facet="java" version="1.8"/>
  <installed facet="jst.web" version="3.0"/>
  <installed facet="wst.jsdt.web" version="1.0"/>
</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 :

Image non disponible

III-A. Configuration du contexte Spring

La structure du projet pour les prochaines étapes est :

Image non disponible

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.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
package fr.idak.tutorial.ws.config;

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

@Configuration
@ComponentScan(value = {"fr.idak.tutorial.ws.repository"})
public class ContextConfig {

}

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.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
package fr.idak.tutorial.ws.config;

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;

@Configuration
@ComponentScan(value = {"fr.idak.tutorial.ws.repository"})
public class ContextConfig {

    @Bean
    public DataSource dataSource(){
        EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
        builder.addScript("db/schema.sql");
        builder.addScript("db/data.sql");
        return  builder.build();
    }

    @Bean
    public JdbcTemplate jdbcTemplate(){
        return new JdbcTemplate(dataSource());
    }

}

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
Sélectionnez
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
Sélectionnez
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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
package fr.idak.tutorial.ws.model;

import java.math.BigDecimal;
import java.util.Date;

public class Book {
    private Long bookId;
    private String isbn;
    private String title;
    private BigDecimal price;
    private Date dtCreate;
    private Date dtUpdate;
// getter et setter ...
}

Créer l'interface BookRepository :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
package fr.idak.tutorial.ws.repository;

import java.util.List;
import fr.idak.tutorial.ws.model.Book;

public interface BookRepository {
    /**
     * Permet de récupérer tous les enregistrements de la table BOOK
     * @return Liste de Book
     */
    List<Book> getAll();

    /**
     * Permet de rechercher un BOOK à partir de son identifiant
     * @param id
     * @return Book
     */
    Book get(Long id);

    /**
     * Permet d'ajouter un nouvel enregistrement de la table BOOK
     * @param book
     */
    void create(Book book);

    /**
     * 
     * Permet de supprimer un BOOK
     * @param book
     */
    void delete(Book book);

    /**
     * Permet de mettre à jour un BOOK 
     * @param book
     */
    void update(Book book);

}

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.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
package fr.idak.tutorial.ws.repository;

import java.util.List;
import javax.annotation.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import fr.idak.tutorial.ws.model.Book;

@Repository
public class BookRepositoryImpl implements BookRepository {

    @Resource
    private JdbcTemplate jt;

    @Override
    public Book get(Long id) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public List<Book> getAll() {
        // TODO Auto-generated method stub
        return null;
    }


    @Override
    public void create(Book article) {
        // TODO Auto-generated method stub
    }

    @Override
    public void delete(Book article) {
        // TODO Auto-generated method stub
    }

    @Override
    public void update(Book article) {
        // TODO Auto-generated method stub
    }
}

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.
 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
package fr.idak.tutorial.ws.repository;

import java.util.List;
import javax.annotation.Resource;
import org.junit.Assert;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.*;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import fr.idak.tutorial.ws.config.ContextConfig;
import fr.idak.tutorial.ws.model.Book;
import fr.idak.tutorial.ws.repository.BookRepository;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={ContextConfig.class})
public class BookRepositoryTest {

    @Resource
    private BookRepository bookRepository;

    @Test
    public void getBook(){
        Long id = 1L;
        Book books = bookRepository.get(id);
        Assert.assertNotNull(books);
        Assert.assertThat(id, is(books.getBookId()));
    }

    @Test
    public void readAll(){
        List<Book> books = bookRepository.getAll();
        Assert.assertNotNull(books);
        Assert.assertTrue(books.size() > 0);
    }
}

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 :

Image non disponible

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 :

 
Sélectionnez
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
@Override
public Book get(Long id) {
      String GET_ARTICLE_BY_ID = "select * from book where book_id=?";
      return  jt.queryForObject(GET_ARTICLE_BY_ID, BeanPropertyRowMapper.newInstance(Book.class), id) ;
}

@Override
public List<Book> getAll() {
    String GET_ALL_ARTICLES = "select * from book";
    return jt.query(GET_ALL_ARTICLES, BeanPropertyRowMapper.newInstance(Book.class));
}

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

Image non disponible

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 :

Image non disponible

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.
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
package fr.idak.tutorial.ws.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@EnableWebMvc
@Configuration
@ComponentScan(basePackages={"fr.idak.tutorial.ws.web"})
public class WebMvcConfig  extends WebMvcConfigurerAdapter{

}
WebAppInitialiser : joue le rôle du fichier de déploiement web.xml standard.
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
package fr.idak.tutorial.ws.config;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

public class WebAppInitializer implements WebApplicationInitializer{

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
         AnnotationConfigWebApplicationContext rootContext = 
                           new AnnotationConfigWebApplicationContext();
            
   //Déclaration de la classe de configuration 
   //du contexte (dataSource, repositories, spring-security …)
   rootContext.register(ContextConfig.class);
   servletContext.addListener(new ContextLoaderListener(rootContext));
   
   //Création du distapcher de servlet
   AnnotationConfigWebApplicationContext dispatcherServlet = 
                               new AnnotationConfigWebApplicationContext();
   dispatcherServlet.register(WebMvcConfig.class);

   //Déclaration du distapcher de servlet
   ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", 
                                      new DispatcherServlet(dispatcherServlet));
   dispatcher.setLoadOnStartup(1);
   dispatcher.addMapping("/");
  }
}

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.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
package fr.idak.tutorial.ws.web.controller;

import java.util.List;
import javax.annotation.Resource;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import fr.idak.tutorial.ws.model.Book;
import fr.idak.tutorial.ws.repository.BookRepository;

@RestController
@RequestMapping(value="/api")
public class BookController {

    @Resource
    private BookRepository bookRepository;

    @RequestMapping(value="/book/{id}", method=RequestMethod.GET)
    public Book getBook(@PathVariable Long id){
        return bookRepository.get(id);
    }

    @RequestMapping(value="/books", method=RequestMethod.GET)
    public List<Book> getBooks(){
        return bookRepository.getAll();
    }
}

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/.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
package fr.idak.tutorial.ws.web.controller;

import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import fr.idak.tutorial.ws.config.ContextConfig;
import fr.idak.tutorial.ws.config.WebMvcConfig;
import fr.idak.tutorial.ws.repository.BookRepository;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {ContextConfig.class, WebMvcConfig.class})
@WebAppConfiguration
public class BookControllerTest {

     @Resource 
     private WebApplicationContext wac;
     
     @Resource
     private BookRepository bookRepositoryMock;
     
     private MockMvc mockMvc;
     
     @Before
     public void init() {
         this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
    }
     
    @Test
    public void checkGetBookByIdUrl() throws Exception{
        mockMvc.perform(MockMvcRequestBuilders.get("/api/book/1"))
               .andExpect(MockMvcResultMatchers.status().isOk());

    }
    @Test
    public void checkGetBooksUrl() throws Exception{
        mockMvc.perform(MockMvcRequestBuilders.get("/api/books"))
               .andExpect(MockMvcResultMatchers.status().isOk());
    }
    
    @Test
    public void getBooks() throws Exception{
        mockMvc.perform(MockMvcRequestBuilders.get("/api/books"))
               .andExpect(MockMvcResultMatchers.status().isOk())
               .andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(11)));
    }
}

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.

Image non disponible

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 :

Image non disponible
  • 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 :

Image non disponible

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.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

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.