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> :
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> :
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 :
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>
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>
84.
85.
86.
87.
88.
<dependency>
<groupId>
org.hamcrest</groupId>
<artifactId>
hamcrest-all</artifactId>
<version>
${hamcrest.version}</version>
<scope>
test</scope>
</dependency>
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 :
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 :
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 :
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.
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 :
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.
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.
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.
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)
)
;
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 :
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 :
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.
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.
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 =
1
L;
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 :
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 :
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.
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 :
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{
}
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.
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/.
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.
- 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.