Aller au contenu

⬅️ Retour au sommaire Flutter

Annexe A — Mini backend Spring Boot de référence (API Étudiants)

illustration du fonctionnement de l'application Flutter avec le Back-end Spring

Ce mini-projet Spring Boot sert de backend de référence pour tous les exercices et TP du Jour 4 et 5. Il expose une API REST CRUD sur des étudiants fictifs, ainsi qu’un endpoint d’authentification simplifié pour le Journée 5. Vous pouvez bien sûr utiliser votre propre API Spring Boot à la place, voire même php, C#, python, tant qu’elle respecte les mêmes routes.

Dépendances (pom.xml)

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.11.5</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-impl</artifactId>
        <version>0.11.5</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId>
        <version>0.11.5</version>
        <scope>runtime</scope>
    </dependency>
</dependencies>

application.properties

server.port=8080
server.address=0.0.0.0
spring.datasource.url=jdbc:h2:mem:etudiantsdb
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true

server.address=0.0.0.0 est indispensable pour que le serveur soit joignable depuis un téléphone physique sur le réseau local (par défaut, Spring Boot n’écoute que sur localhost).

Entité Etudiant

@Entity
public class Etudiant {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String nom;
    private String email;
    private String promotion;

    // Getters, setters, constructeurs
    public Etudiant() {}
    public Etudiant(String nom, String email, String promotion) {
        this.nom = nom; this.email = email; this.promotion = promotion;
    }
    public Long getId() { return id; }
    public String getNom() { return nom; }
    public void setNom(String nom) { this.nom = nom; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    public String getPromotion() { return promotion; }
    public void setPromotion(String promotion) { this.promotion = promotion; }
}

Repository

public interface EtudiantRepository extends JpaRepository<Etudiant, Long> {
}

Contrôleur REST

@RestController
@RequestMapping("/api/etudiants")
@CrossOrigin(origins = "*") // utile uniquement pour Flutter Web autorise toutes les URLs !
public class EtudiantController {

    private final EtudiantRepository repository;

    public EtudiantController(EtudiantRepository repository) {
        this.repository = repository;
    }

    @GetMapping
    public List<Etudiant> getTous() {
        return repository.findAll();
    }

    @GetMapping("/{id}")
    public ResponseEntity<Etudiant> getParId(@PathVariable Long id) {
        return repository.findById(id)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Etudiant creer(@RequestBody Etudiant etudiant) {
        return repository.save(etudiant);
    }

    @PutMapping("/{id}")
    public ResponseEntity<Etudiant> modifier(@PathVariable Long id, @RequestBody Etudiant maj) {
        return repository.findById(id).map(existant -> {
            existant.setNom(maj.getNom());
            existant.setEmail(maj.getEmail());
            existant.setPromotion(maj.getPromotion());
            return ResponseEntity.ok(repository.save(existant));
        }).orElse(ResponseEntity.notFound().build());
    }

    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void supprimer(@PathVariable Long id) {
        repository.deleteById(id);
    }
}

Endpoint d’authentification simplifié (pour jour 5)

@RestController
@RequestMapping("/api/auth")
public class AuthController {

    private static final String SECRET = "cle-secrete-demo-a-ne-jamais-utiliser-en-production";

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest req) {
        // Démo pédagogique : un seul compte "admin" / "password"
        if ("admin".equals(req.username()) && "password".equals(req.password())) {
            String token = Jwts.builder()
                    .setSubject(req.username())
                    .setExpiration(new Date(System.currentTimeMillis() + 3600_000))
                    .signWith(Keys.hmacShaKeyFor(SECRET.getBytes()), SignatureAlgorithm.HS256)
                    .compact();
            return ResponseEntity.ok(Map.of("token", token));
        }
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("message", "Identifiants invalides"));
    }

    public record LoginRequest(String username, String password) {}
}

Données de test (data.sql ou CommandLineRunner)

@Component
public class DonneesInitiales implements CommandLineRunner {
    private final EtudiantRepository repository;

    public DonneesInitiales(EtudiantRepository repository) {
        this.repository = repository;
    }

    @Override
    public void run(String... args) {
       repository.save(new Etudiant("Nadia Benali", "nadia.benali@esiea.fr", "L3 Info"));
       repository.save(new Etudiant("Karim Haddad", "karim.haddad@esiea.fr", "M1 Info"));
       repository.save(new Etudiant("Léa Nguyen", "lea.nguyen@esiea.fr", "L3 Info"));
       repository.save(new Etudiant("Moussa Traoré", "moussa.traore@esiea.fr", "M2 Info"));
       repository.save(new Etudiant("Inès Cherif", "ines.cherif@esiea.fr", "L3 Info"));
       repository.save(new Etudiant("Yanis Barbosa", "yanis.barbosa@esiea.fr", "M1 Info"));
       repository.save(new Etudiant("Fatou Diallo", "fatou.diallo@esiea.fr", "L3 Info"));
       repository.save(new Etudiant("Lucas Mendes", "lucas.mendes@esiea.fr", "M2 Info"));
       repository.save(new Etudiant("Camille Dupont", "camille.dupont@esiea.fr", "L3 Info"));
       repository.save(new Etudiant("Mamadou Sow", "mamadou.sow@esiea.fr", "M1 Info"));
       repository.save(new Etudiant("Jade Rakotoarivony", "jade.rakotoarivony@esiea.fr", "L3 Info"));
       repository.save(new Etudiant("Enzo Ribeiro", "enzo.ribeiro@esiea.fr", "M2 Info"));
    }
}

Lancer le backend

mvn spring-boot:run

Testez avec curl avant de brancher Flutter :

curl http://localhost:8080/api/etudiants
curl -X POST http://localhost:8080/api/etudiants \
  -H "Content-Type: application/json" \
  -d '{"nom":"Eve Blanc","email":"eve.blanc@esiea.fr","promotion":"L3 Info"}'

Puis, depuis Flutter, utilisez l’URL adaptée à votre environnement de test (voir Chapitre 4, section 2).

⬅️ Retour au sommaire Flutter