En quelques années, Spring boot s’est imposé dans l’univers des microservices Java. Il a accéléré la vitesse de développement des applications web et surtout les web services. Avec spring boot, vous pouvez aller aussi rapidement comme vous le voulez et il ne vous impose aucune manière spécifique de procéder. En matière de rapidité, spring boot fait parti des meilleurs outils en Java. Malgré tous ces éléments, le framework reste toujours un peu absent dans la communauté francophone.

Dans ce tutoriel, nous allons voir comme concevoir un API simple avec spring boot. Nous n’allons par faire usage d’une base de données mais juste des structures de données en collection pour stocker les données manipulés.

Installation de spring boot

Avant de commencer par programmer en Spring boot, nous devons d’abord l’installer. Spring boot dispose d’un installeur appélé spring initializr. Le tutoriel suivant vous renseigne sur la manière d’initialiser un projet spring boot.

Le projet que nous allons créer nous permettra de gerer une collection de sites webs. Dans le cas pratique, on va supposer que nous voulons faire une collection de sites webs populaires dans un secteur donné et laisser le choix aux internautes de voter pour le site dont ils pensent devoir être au sommet. Le classement se fera en fonction du nombre de votes qu’il site a collecté.

Pour commencer, veuillez initialiser le projet spring avec les coordonnées suivantes:

Éléments Valeurs
Groupcom.ultron.systems
Artifactbuzzsites-api
Namebuzzsites
DescriptionClassement des sites qui font le buzz actuellement
Package namecom.ultron.systems.buzzsites
Paramètres d’initialisation

Après avoir renseigné les coordonnées du projet, utilisez les dépendances suivantes pour générer le projet:

  • Spring Web

C’est tout ce que nous allons utiliser pour le moment.

Vous pouvez également télécharger ou cloner le repository github du projet en utilisant le lien ci-dessous:

https://github.com/ultron-systems/buzzsites

Après avoir généré ou cloné le projet, importez-le dans votre éditeur favori. Lancez la compilation et exécutez le projet.

Ensuite, allez dans votre navigateur le tapez l’adresse suivante: http://127.0.0.1:8080 . Vous aurez un visuel semblable à celui qui suit.

Capture d'écran d'erreur spring par défaut lorsqu'aucun controlleur de gère la page d'index
White label error page

Félicitations, vous avez fait une installation qui marche. L’erreur précedente indique qu’actuellement, nous avons pas encore de ressource qui s’exécute à l’index c’est-à-dire le /.

Voici quelques unes des erreurs que vous pouvez avoir:

  • Un autre serveur écoute déjà le port 8080 (Arrêtez-le!)
  • Java n’est pas configuré dans votre éditeur (Configuez-le!)

“Hello world!” avec spring boot

Maintenant que nous avons spring boot installé, nous allons créer notre première ressource web! Cool n’est-ce pas ? Pour ce faire, allez dans le package par défaut du projet et ajoutez la classe ApiController comme ce que suit:

package com.ultron.systems.buzzsites;

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

@RestController
public class ApiController {

    @GetMapping(value = "/")
    public String index() {
        return "Bonjour tout le monde!";
    }
}

Recompilez et exécutez le projet. Puis, retapez encore l’adresse http://127.0.0.1:8080. L’erreur précédente a disparu vous laissant le message “Bonjour tout le monde!”. Qu’avons-nous fait donc ?

Nous avons ajouter la classe ApiController comme étant une ressource Web. A la base ApiController n’est qu’une simple classe. Ce qui fait de lui une ressource web est l’annotation @RestController.

Dans la resource ApiController, on a une méthode index qui est mappée sur le chemin /. Ce qui veut dire tout simplement qu’à chaque fois que l’on tapera / en suite de l’adresse du serveur oû on aurait déployé l’API, la méthode index de ApiController sera automatiquement invoquée par Spring boot.

Pour pratiquer ce qui précède, nous allons ajouter une autre méthode qui s’exécutera à chaque fois qu’on tapera /contact.

package com.ultron.systems.buzzsites;

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

@RestController
public class ApiController {

    @GetMapping(value = "/")
    public String index() {
        return "Bonjour tout le monde!";
    }

    @GetMapping(value = "/contact")
    public String contact() {
        return "Page de contact";
    }
}

Voici le résultat lorsque nous tapons http://127.0.0.1:8080/contact

Page de test avec un lien simple spring boot
Lien de la page de contact

Plan d’attaque

Maintenant que nous avons vu comment lier nos méthodes aux routes, nous avons les bases nécessaires pour aborder la ressource principale du projet. Nous allons déclarer une classe BuzzSite qui contiendra les caractéristiques d’un site que bous voulons ajouter et une classe BuzzController qui va représenter la ressource qui gérer les objets de la classe BuzzSite. Une fois ce process terminé, nous allons implémenter les fonctionnalités suivantes:

  • La liste des buzz sites
  • L’affichage d’un buzz site
  • L’ajout d’un buzz site
  • La suppression d’un buzz site
  • La modification d’un buzz site

La class BuzzSite

Pour décrire un site qui fait du buzz, nous allons utiliser principalement trois attributs pour un début:

  • L’url du site (String)
  • la description du site (String)
  • le nombre de votes ascendants. (Long)
  • Un identifiant (Integer)

Avant de déclarer la classe, créez d’abord un package entity de référence complète:

com.ultron.systems.buzzsites.entity

Voici la classe obtenue initialement:

package com.ultron.systems.buzzsites.entity;

public class BuzzSite {
    private Integer id;
    private String url;
    private String description;
    private Long upvotes = 0L;

    public BuzzSite() {
    }

    public BuzzSite(Integer id, String url) {
        this.id = id;
        this.url = url;
    }
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Long getUpvotes() {
        return upvotes;
    }

    public void setUpvotes(Long upvotes) {
        this.upvotes = upvotes;
    }
}

Dans le code précedent, nous avons ajouté une classe modèle avec les informations que nous voulons retenir sur un site qui fait du buzz.

La liste des buzz sites

Maintenant, nous allons ajouter une classe ressource qui nous permettra de gérer la liste des sites, l’ajout, la modification et la suppression des sites. Dans ce cas aussi, nous allons ajouter un nouveau package controller et mettre la classe BuzzSiteController dedans.

package com.ultron.systems.buzzsites.controller;

import com.ultron.systems.buzzsites.entity.BuzzSite;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping(path = "buzz-sites")
public class BuzzSiteController {
    /**
     * Liste des buzz sites
     * @return List
     */
    @GetMapping
    public List<BuzzSite> index() {
        return new ArrayList<>();
    }
}

La classe précédente déclare une ressource web qui est accessible en lien relatif /buzz-sites comme dans http://127.0.0.1:8000/buzz-sites par exemple. C’est ce que fait les annotations RestController et @RequestMapping(path = “buzz-sites”).

Ensuite, la méthode index portant l’annotation @GetMapping signifie qu’elle sera invoquée lorsque nous tapperons uniqument /buzz-sites en mode HTTP GET.

Récupération de la liste des objets avec spring boot
GET http://127.0.0.1:8080/buzz-sites

Pour le moment le body de la résponse HTTP montre un tableau vide. Ajoutons donc quelques sites et modifions le test.

package com.ultron.systems.buzzsites.controller;

import com.ultron.systems.buzzsites.entity.BuzzSite;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping(path = "buzz-sites")
public class BuzzSiteController {

    private List<BuzzSite> sites;

    public BuzzSiteController() {
        this.sites = new ArrayList<>();
        this.sites.add(new BuzzSite(1, "https://amazon.com"));
        this.sites.add(new BuzzSite(2, "https://twitter.com"));
        this.sites.add(new BuzzSite(3, "https://facebook.com"));
    }

    /**
     * Liste des buzz sites
     * @return List
     */
    @GetMapping
    public List<BuzzSite> index() {
        return this.sites;
    }
}

Dans l’exemple suivant, nous avons ajouté un attribut sites avec un constructeur qui initialise l’attribut et lui donne 3 valeurs par défaut. Puis nous avons modifié la méthode index pour retourner la liste.

[
    {
        "id": 1,
        "url": "https://amazon.com",
        "description": null,
        "upvotes": null
    },
    {
        "id": 2,
        "url": "https://twitter.com",
        "description": null,
        "upvotes": null
    },
    {
        "id": 3,
        "url": "https://facebook.com",
        "description": null,
        "upvotes": null
    }
]

Le code précédant montre le résultat obtenu lors de l’exécution de la méthode. Notons que l’attribut upvotes rammene la valeur null alors que ça devrait rammener une valeur entière comme un 0 par exemple. Pour régler cela, nous allons retourner dans la classe BuzzSite pour initialiser l’attribut upvotes à 0 comme ceci:

private Long upvotes = 0L;

Voici le résultat obtenu à l’execution:

[
    {
        "id": 1,
        "url": "https://amazon.com",
        "description": null,
        "upvotes": 0
    },
    {
        "id": 2,
        "url": "https://twitter.com",
        "description": null,
        "upvotes": 0
    },
    {
        "id": 3,
        "url": "https://facebook.com",
        "description": null,
        "upvotes": 0
    }
]

L’affichage d’un buzz site

Dans la partie précédente, nous avons vu comment recupérer la liste des buzz sites. Ici cette partie, nous allons afficher les détails d’un buzz site. La classe BuzzSite, contient un attribut id qui permet d’identifier de façon unique un site. Nous allons donc nous baser sur cet attribut pour rechercher de façon unique un site. Pour ce faire, nous allons ajouter une nouvelle méthode dans la resource BuzzSiteController comme dans ce qui suit.

/**
     * Récupérer les détails d'un site
     * @param id
     * @return
     */
    @GetMapping(value = "{id}")
    public BuzzSite details(@PathVariable Integer id) {
        return this.sites.stream()
            .filter(buzzSite -> buzzSite.getId() == id).findFirst().get();
    }

Dans le code précédant, l’annotation @GetMapping et l’attribut value = “{id}” permettent de spécifier le fait que la méthode details serait appelé à chaque fois que l’on effectue une requête GET /buzz-sites/{id}. Comme par exemple GET /buzz-sites/1 pour récupérer les informations du site amazon.com etc. Le format {id} est une variable de route qui permet de dire que nous pouvons passer à sa place n’importe quel entier. Comment savons-nous que le type doit être entier? Le type du parametre id de la méthode details. L’annotation @PathVariable à côté signifie juste que la valeur qui aurait été fournie pour la variable de route id doit-être recupérée et injectée dans le paramètre id. Si le paramètre et la variable de route portent le même nom, l’annotation @PathVariable n’a pas besoin de prendre un attribut. Au cas contraire on doit renseigner l’attribut value comme dans l’exemple quit suit.

 /**
     * Récupérer les détails d'un site
     * @param ident
     * @return
     */
    @GetMapping(value = "{id}")
    public BuzzSite details(@PathVariable(value = "id") Integer ident) {
        return this.sites.stream()
            .filter(buzzSite -> buzzSite.getId() == ident).findFirst().get();
    }

Pour éviter toute forme de confusion et prôner la lisibilité, vous pouvez choisir à chaque fois de toujours donner le même nom aux deux.

A l’exécution, d’un GET /buzz-sites/1 dans postman, on obtient le résultat de la capture suivante.

{
    "id": 1,
    "url": "https://amazon.com",
    "description": null,
    "upvotes": 0
}

L’ajout d’un buzz site

Pour ajouter un site à la lite de nos sites nous allons effectuer une requête POST /buzz-sites avec pour contenu, les détails du site à ajouter.

 /**
     * Ajouter un BuzzSite à la liste des sites
     * @param site
     * @return
     */
    @PostMapping
    public BuzzSite ajouter(@RequestBody BuzzSite site) {
        site.setId(sites.size());
        sites.add(site);
        return site;
    }
L'ajout d'un objet à la liste des objets avec spring boot
Ajouter un site

Après cet ajout, une nouvelle exécution de GET /buzz-sites donne le résultat suivant:

[
    {
        "id": 1,
        "url": "https://amazon.com",
        "description": null,
        "upvotes": 0
    },
    {
        "id": 2,
        "url": "https://twitter.com",
        "description": null,
        "upvotes": 0
    },
    {
        "id": 3,
        "url": "https://facebook.com",
        "description": null,
        "upvotes": 0
    },
    {
        "id": 4,
        "url": "https://gmail.com",
        "description": "Créer des emails avec google",
        "upvotes": 10
    }
]

La suppression d’un buzz site

Pour supprimer un site de la liste des sites, nous allons ajouter une nouvelle méthode à la resource BuzzSiteController qui s’exécutera à la requête DELETE /buzz-sites/{id}. Comme par exemple DELETE /buzz-sites/4 pour supprimer l’élément dont l’id est 4. Nous avons choisi de retourner la liste de sites restants pour immédiatement constater la suppression et simplifier les choses.

/**
     * Supprimer un buzz site de la liste
     * @param id
     * @return
     */
    @DeleteMapping(value = "{id}")
    public List<BuzzSite> supprimer(@PathVariable Integer id) {
        return this.sites.stream()
            .filter(buzzSite -> buzzSite.getId() != id)
            .collect(Collectors.toList());
    }

La modification d’un buzz site

Pour modifier un site, nous allons faire une requête PUT /buzz-sites/{id} vers la resource concernée et passer les nouvelles informations au serveur. Le code suivant donne un exemple de procédure de modification implémentée dans BuzzSiteController. Encore une fois, nous avons choisi de retourner la liste des sites après modification pour constater les mises à jours.

/**
     * Modifier un site
     * @param id
     * @return
     */
    @PutMapping(value = "{id}")
    public List<BuzzSite> modifier(@PathVariable Integer id, @RequestBody BuzzSite site) {
        // On supprime l'ancien de la liste
        List<BuzzSite> lesAutres = sites.stream()
                .filter(buzzSite -> buzzSite.getId() != id)
                .collect(Collectors.toList());
        // On ajoute la modification à la fin
        site.setId(id);
        lesAutres.add(site);
        this.sites = lesAutres;
        return sites;
    }
Résultat de la requête de modification d'un objet avec Spring boot
La requête de modification

Le résultat JSON donne ce qui suit.

[
    {
        "id": 1,
        "url": "https://amazon.com",
        "description": null,
        "upvotes": 0
    },
    {
        "id": 3,
        "url": "https://facebook.com",
        "description": null,
        "upvotes": 0
    },
    {
        "id": 2,
        "url": "https://twitter.com",
        "description": "Pour tweeter les information",
        "upvotes": 10
    }
]

Conclusion

Dans ce tutoriel, nous avons voulu vous faire constater comment il est simple d’implémenter un CRUD avec Spring boot. Veuillez nous faires parvenir vous remarques et suggestions si vous en avez. N’hésitez pas à nous contacter pour vous assister en cas de problèmes.