tete du loic

 Loïc YON [KIUX]

  • Enseignant-chercheur
  • Référent Formation Continue
  • Responsable des contrats pros ingénieur
  • Référent entrepreneuriat
  • Responsable de la filière F2 ingénieur
  • Secouriste Sauveteur du Travail
mail
loic.yon@isima.fr
phone
(+33 / 0) 4 73 40 50 42
location_on
ISIMA
  • twitter
  • linkedin
  • viadeo

eco ZZouvenir money

Date de première publication : 2026/01/07

L'objectif de ce TP est d'exposer une API REST (de type HATEOAS) de données stockées dans une base non relationnelles comme MongoDB.

Les données exposées concernent la gestion de collections/d'albums de "billets souvenir".

Billet
Billet

Mongo DB

Les machines à l'ISIMA sont configurées pour vous permettre d'utiliser docker. Si vous voulez travailler sur votre propre machine, il vous faudra installer ce qu'il faut. Par exemple, sous MacOS, il faut installer Docker Desktop

Pour utiliser docker à l'ISIMA, vous devez toutefois lancer :


docker-rootless-init.sh

docker pull mongo:latest

docker run -d \
  --name mongodb \ 
  -p 27017:27017 \ 
  -v mongodb_data:$HOME/Documents/MONGO \ 
  mongo:latest

Voici quelques commandes docker pour la gestion de votre image :


docker ps -a

docker stop mongodb
docker start mongodb

docker rm mongodb # donnees conservees
docker volume rm mongodb_data # suppression des donnees

J'aime bien manipuler la base MongoDB en ligne avec le shell :


docker exec -it mongodb mongosh

Voici quelques commandes :


show dbs
use base
show collections

db.types.deleteMany({});
db.collections.find();

Vous pouvez également installer Mongo DB Compass qui propose une interface graphique pour manipuler les "bases"

Mise en place du projet

Il faut créer un nouveau projet Spring Boot et choisir les options suivantes :

On a sélectionné les options suivantes :

Dans le fichier application.properties, il faudra dire quelle base utiliser (on peut spécifier utilisateur et mot de passe entre autres) :


spring.mongodb.uri=mongodb://localhost:27017/zzouvenir

Nous allons avoir besoin de décrire les données à mettre en base :

Avec les options SpringBoot choisies, toutes les classes sont des Document - les instances peuvent être exposées.

Chaque document a besoin d'un identifiant , soit au travers d'un attribut annoté @Id, soit un champ _id calculé automatiquement par MongoDB.

La base MongoDB ne gère pas les relations comme pourrait le faire une base SQL et notamment les suppressions en cascade par exemple.

On peut gérer la notion de "clé étrangère" avec les annotations @DocumentReference (NOSQL - à privilégier), @DBRef (NOSQL) ou encore @OneToMany (objet).

Voici donc un squelette de code pour gérer tout cela : (n'hésitez pas à utiliser Lombok pour générer les méthodes)


import org.springframework.data.annotation.Id;

public class BilletType {

    @Id
    String id;
    String annee;
    String amorce;
    String numero;

    public BilletType(String amorce, String annee, String numero) {
        this.id = amorce+annee+numero;
        this.annee = annee;
        this.amorce = amorce;
        this.numero = numero;
    }

    @Override
    public String toString() {
       return String.format(
          "BilletType[id=%s, amorce='%s', annee='%s', numero='%s' ]",
                  id, amorce, annee, numero);
    }
}

import org.springframework.data.annotation.Id;
import java.util.*;

public class Album {
    @Id
    String id;
    List<Billet> billets;

    public Album(String id) {
        this.id = id;
        billets = new ArrayList<Billet>();
    }

    public void add(Billet billet) {
        billets.add(billet);
    }
}

Pour finir, la classe Billet :


public class Billet {

    String id;
    BilletType type;
    String numero;

    public Billet(BilletType type, String numero) {
        this.id = type.getId()+numero;
        this.type = type;
        this.numero = numero;
    }
}

Il faut encore écrire les repositories BilletTypeRepository et AlbumRepository.

La classe Billet n'a pas de repository, ce n'est pas un document. Les informations de billet sont embarquées dans la collection/album.


import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

// annotation optionnelle, sauf si on veut changer le nom de publication
@RepositoryRestResource(collectionResourceRel = "types", path = "types")
public interface BilletTypeRepository extends MongoRepository<BilletType, String> {
}  

On doit encore tester que cela marche. Créons quelques données :


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ZZouvenirApplication  implements CommandLineRunner {

    @Autowired
    BilletTypeRepository types;

    @Autowired
    AlbumRepository albums;

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

    @Override
    public void run(String... strings) throws Exception {
        albums.deleteAll();
        types.deleteAll();

        BilletType type1, type2;
		    types.save(new BilletType("UEGS", "2016", "1"));
		    types.save(new BilletType("UEGS", "2026", "2"));
		    types.save(new BilletType("UEGS", "2017", "3"));
		    types.save(new BilletType("UEGS", "2018", "4"));
		    types.save(new BilletType("UEGS", "2019", "5"));
		    types.save(new BilletType("UEGS", "2023", "6"));
		    types.save(new BilletType("UEGS", "2018", "7"));
		    types.save(new BilletType("UEGS", "2024", "8"));
		    types.save(type1 = new BilletType("UEGS", "2025", "9"));
		    types.save(type2 = new BilletType("UEQZ", "2020", "1"));
		

        Album album = new Album("loic");

        Billet billet = new Billet(type1, "000001");
        album.add(billet);
        album.add(new Billet(type2, "000002"));
        albums.save(album);

    
        System.out.println("BilletTypes found with findAll():");
        System.out.println("-------------------------------");
        for (BilletType btype : types.findAll()) {
            System.out.println(btype);
        }
	}
}

On peut vérifier que la base est bien peuplée avec CURL ou un navigateur :


curl http://localhost:8080
curl http://localhost:8080/types
curl http://localhost:8080/albums
curl http://localhost:8080/albums/loic

curl -i -X POST -H "Content-Type:application/json" -d "{  \"id\" : \"autre\" }" http://localhost:8080/albums

Par défaut, tous les repositories sont publiés. On peut se limiter à ceux qui sont annotés avec @RepositoryRestResource, il faut alors ajouter la configuration suivante : spring.data.rest.detection-strategy=annotated

On peut aussi interdire un type d'action à réaliser, soit en écrivant une configuration :


@Configuration 
public class RestConfig implements RepositoryRestConfigurer { 
   @Override 
   public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) { 
      config.getExposureConfiguration() 
        .forDomainType(BilletType.class) .withItemExposure((metadata, httpMethods) -> httpMethods.disable(HttpMethod.DELETE)) 
        .withCollectionExposure((metadata, httpMethods) -> httpMethods.disable(HttpMethod.DELETE)); 
  } 
}
// le code ci dessus concerne la classe (dite métier ou domaine) et les méthodes que l'on ne veut pas exposer.

soit en annotant un repository ou une de ses méthodes :


@RepositoryRestResource
public interface BilletTypeRepository extends ... {

    @Override
    @RestResource(exported = false)
    void deleteById(String id);

    @Override
    @RestResource(exported = false)
    void delete(BilletType entity);
}

Utilisation de l'API Rest

On peut écrire (ou faire écrire à une IA) une page HTML qui récupère avec de l'AJAX les éléments stockés en base

Si la page web n'est pas hébergée sur le même serveur que celui de l'API, il faudra permettre la consultation en désactivant la sécurité du navigateur. Un des moyens est de positionner une annotation @CrossOrigin sur les repositories consultés.


import org.springframework.web.bind.annotation.CrossOrigin;

Allez plus loin

Il faudrait maintenant sécuriser l'API pour que seuls certains utilisateurs (avec des rôles) puissent accéder aux informations.

Liens