Faire simplement des webservices RestFul avec Spring-Data-Rest et Tester avec RestTemplate

whats-new-in-spring-data logo-spring-io

 

Comment réaliser des web services Rest (vraiment RestFul) sans écrire du code ?

Avec Spring-Data-Rest, c'est possible!

Pour être précis, on peut exposer les entités de la base de données via le protocole Rest tout en respectant le principe HATEOAS, et tout cela en déclarant uniquement des interfaces.

Comment faire ?

Ce billet tente  de répondre par la pratique.

Néanmoins il exige des pré-requis:

  • Connaître un peu Spring-Boot et Spring-Data.

Voici toutes les briques nécessaires à la mise en place des web services Rest avec l'écosystème de Spring:

  • Spring-Boot (ceci est optionnel mais facilite notre démo. On peut aussi rajouter spring-data-rest à un projet springMVC existant),
  • Spring-Data
  • Spring-Data-Rest

 

La démo expose la liste des salariés de Netapsys Conseil en suivant les étapes qui sont détaillées ci-dessous :

ÉTAPE 1. Configurer le projet et ses dépendances

Il n'y a rien à faire dans cette étape, on délègue tout à Spring-Boot.

ÉTAPE 2. Écrire la classe entité Salarie

Une seule Classe (POJO ou entité) propre au métier.

ÉTAPE 3.  Déclarer le Repository

C'est cette partie qui utilise Spring-Data. On déclare son interface Repository assurant la couche d'accès aux données (DAO).

ÉTAPE 4. Annoter la méthode pour l'exposer en Rest

C'est ici que Spring-Data-Rest opère.

ÉTAPE 5. Tester avec RestTemplate

Cette étape est optionnelle, il s'agit d'écrire une classe JUnit pour tester notre web service Rest via RestTemplate de Spring.

 

Détaillons donc chacune de ces étapes.

ÉTAPE 1Configurer le projet maven et ses dépendances

Dans notre démo on s'appuie complètement sur spring-Boot pour la configuration du projet.

Nous partons de la page web Initializr offerte par Spring donnant la possibilité de générer notre projet démo.

Sur cette page, saisir les noms du group, de l'artifact ... Saisir aussi la version de Spring-Boot ( 1.3.0.M5 pour notre démo).

Pour plus de clarté pour la suite, il est préférable de fixer les Artifact, le Name et le Package Name à la valeur "sprestdata"

puis de choisir le Type=Maven Project et java version 1.8.

Cocher aussi les quatre cases JPA, H2 (ou tout autre base) et "Rest Repositories"  puis cliquer sur le bouton "Generate project".

Dézipper l'archive générée puis importer le projet dans votre IDE préféré.

Vous pouvez constater que le pom.xml déclare les dépendances (les starter de spring-Boot) utiles. Par exemple, on a ceci:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
	<groupId>com.h2database</groupId>
	<artifactId>h2</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-test</artifactId>
	<scope>test</scope>
</dependency>
pom

 

Vérifiez que ces propriétés sont présentes dans le pom:

<properties>
<project.build.sourceEncoding>UTF8
 </project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>

Et vous pouvez aussi remarquer la présence du plugin spring-Boot:

<build>
 <plugins>
	<plugin>
	 <groupId>org.springframework.boot</groupId>
	 <artifactId>spring-boot-maven-plugin</artifactId>
	</plugin>
</plugins>
	</build>
plugin

Enfin, observez la classe générée et annotée @SpringBootApplication nommée "SprestdataApplication.java" et une classe de test.

 

ÉTAPE 2Écrire la classe métier Salarie

Simple classe entité annotée avec @Entity.

import javax.persistence.*;

@Entity
public class Salarie{
    @Id   @GeneratedValue
    private Long id;
    private String name;
    private String service;

    public Salarie(){}

    public Salarie(final String name){
        this.name=name;
    }
.....//getters setters toString omis
}
Salarie

 

ÉTAPE 3Déclarer l'interface Repository

Nous partons de la classe générée à l'étape 1 nommée SprestdataApplication  et on ajoute la déclaration du SalarieRepository.

Ainsi le code de la classe devient:

@SpringBootApplication
public class SprestdataApplication {

    public static void main(String[] args) {
        SpringApplication.run(SprestdataApplication.class, args);
    }

interface  SalarieRepository extends JpaRepository<Salarie,Long>{

    List<Salarie> findByName(String name);
}

 

Nous avons juste déclaré dans le Repository une méthode de recherche (query) nommée findByName.

C'est bien cette méthode que nous allons exposer en Rest.

 

ÉTAPE 4Annoter la méthode pour l'exposer en Rest

Dans le code précédent, on annote la méthode avec @RestResource de Spring-Data-Rest comme suit:

 

@SpringBootApplication
public class SprestdataApplication {

    public static void main(String[] args) {
        SpringApplication.run(SprestdataApplication.class, args);
    }

@RepositoryRestResource(collectionResourceRel = "people", path = "salaries")
interface  SalarieRepository extends JpaRepository<Salarie,Long>{
    @RestResource (path="byName")
    List<Salarie> findByName(@Param ("name") String name);
}

Ce qu'il faudrait signaler ici:

1) La méthode findByName est annotée avec @RestResource avec paramètre path=byName ce qui signifie qu'elle serait accessible via l'URI Rest /byName.

On peut passer le paramètre name à l'URI comme par exemple /byName?name=

2) J'ai laissé l'annotation optionnelle @RepositoryRestResource permettant de préciser le path de l'URI de Rest ainsi que le nom data de JSon.

 

ÉTAPE 5Écrire un test Junit avec RestTemplate

Cette étape est optionnelle car on peut tester notre application avec l'url http://localhost:8080/ qui affiche l'ensemble des liens disponibles pour notre démo comme l'illustre cette capture:

{
  "_links" : {
    "people" : {
      "href" : "http://localhost:8888/salaries{?page,size,sort}",
      "templated" : true
    },
    "profile" : {
      "href" : "http://localhost:8888/profile"
    }
  }
}

On voit que le lien http://localhost:8080/salaries est disponible, si on saisie cet URL dans le browser on obtient la liste des salaries de la base comme le montre la capture:

{
      "name" : "salarie000",
      "service" : null,
      "_links" : {
        "self" : {
          "href" : "http://localhost:8888/salaries/1"
        },
        "salarie" : {
          "href" : "http://localhost:8888/salaries/1"
        }
      }
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:8888/salaries"
    },
    "profile" : {
      "href" : "http://localhost:8888/profile/salaries"
    },
    "search" : {
      "href" : "http://localhost:8888/salaries/search"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 20,
    "totalPages" : 1,
    "number" : 0
  }
}

 

Et on peut aussi saisir l'URL Rest: http://localhost:8888/salaries/search/byName?name=Salarie000

On préfère plutôt écrire une classe de test Junit pour (intégration continue) valider notre application.

La classe Junit générée par Spring-Boot à l'étape 1 sera complétée ainsi:

 

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SprestdataApplication.class)
@WebAppConfiguration
public class SprestdataApplicationTests {

	final static String NAME_SALARIE="salarie000";
	final static String HOST_PORT="http://localhost:8080";
	@Autowired SalarieRepository sr;

	RestTemplate restTemplate=new RestTemplate();

	@Before
	public void before(){
		sr.save(new Salarie(NAME_SALARIE));
	}

	@Test
	public void testSearchOneSalarie(){
		ResponseEntity<String> response =	restTemplate.getForEntity(HOST_PORT+"/salaries/search/byName?name="+NAME_SALARIE, String.class);
		System.out.println(response);
		Assert.assertEquals( response.getStatusCode(), HttpStatus.OK);
		Assert.assertTrue(response.toString().contains(NAME_SALARIE) );

	}
}

Pour finir, exécutez le test qui valide le webservice correspondant à la recherche par nom c'est à dire l'URI /byName?name=salarie000.

C'est aussi simple que ça!

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.