⬅️ Retour au sommaire Flutter
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.
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).
server.address=0.0.0.0
localhost
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; } }
public interface EtudiantRepository extends JpaRepository<Etudiant, Long> { }
@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); } }
@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) {} }
data.sql
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")); } }
mvn spring-boot:run
Testez avec curl avant de brancher Flutter :
curl
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).