Java et Spring Boot
Construire une API REST avec Java et Spring Boot : du projet vide au CRUD persistant. Conteneur IoC, contrôleurs, services, JPA, validation et gestion d'erreurs, pas à pas.
À la fin du cours, tu sais
- Comprendre ce qu'apporte Spring Boot par rapport à Java « nu »
- Créer un projet avec Spring Initializr et le lancer
- Maîtriser l'injection de dépendances et le conteneur IoC
- Exposer une API REST avec @RestController
- Découper proprement contrôleur, service et repository
- Persister des données avec Spring Data JPA
- Valider les entrées et gérer les erreurs de façon centralisée
Prérequis
- Bases de Java (classes, méthodes, types, collections)
- Comprendre ce qu'est une API HTTP (verbes GET/POST, statut, JSON)
- Un JDK 21 et un IDE (IntelliJ IDEA ou VS Code) installés
Étape 1 sur 11 : Pourquoi Spring Boot
Chapitre 1
Pourquoi Spring Boot
Spring est le framework backend de référence dans l'écosystème Java. Spring Boot, c'est Spring rendu simple à démarrer.
Java est l'un des langages les plus utilisés au monde en entreprise, et dans son écosystème, Spring est le framework backend de référence — celui que tu retrouveras dans la banque, l'assurance, l'industrie, partout où les applications doivent tourner de façon fiable pendant des années. Mais Spring a longtemps eu une réputation de complexité, avec ses interminables fichiers de configuration XML. Spring Boot est né précisément pour dissiper cette réputation : c'est Spring rendu simple à démarrer.
Le principe qui sous-tend Spring Boot est celui de la « convention plutôt que configuration ». Concrètement, Boot ajoute par-dessus Spring quatre facilités qui changent la vie. La configuration automatique devine les réglages à partir des dépendances présentes, t'évitant d'écrire ce que le framework peut déduire. Le serveur embarqué (Tomcat inclus) fait de ton application un simple java -jar, sans serveur à installer à côté. Les starters regroupent des dépendances cohérentes (spring-boot-starter-web tire tout ce qu'il faut pour une API). Et Spring Boot aligne les versions des bibliothèques entre elles, t'épargnant les conflits de compatibilité.
Un mot sur les versions, car elles conditionnent tes imports. Spring Boot 3 exige Java 17 au minimum et s'appuie sur Jakarta EE : les imports commencent désormais par jakarta. et non plus javax. — un détail qui déroute quand on suit d'anciens tutoriels. Dans ce cours, on part sur le JDK 21, la version de support à long terme (LTS) actuelle. Retiens l'essentiel : Spring structure ton application, Spring Boot te la fait démarrer en quelques minutes, et l'ensemble te met sur les rails des standards attendus en entreprise.
Spring est un framework qui structure une application Java : il gère la création des objets, leurs dépendances, l'accès aux données, la sécurité, le web… Spring Boot ajoute par-dessus une couche de configuration automatique et un serveur embarqué pour qu'un projet démarre en quelques minutes, sans XML interminable.
Ce que Spring Boot t'évite
- Configuration automatique : Spring devine la config à partir des dépendances présentes (« convention plutôt que configuration »)
- Serveur embarqué : Tomcat est inclus, ton appli est un simple
java -jar, pas un serveur à installer à côté - Starters : des dépendances groupées (
spring-boot-starter-web,...-data-jpa) qui tirent tout ce qu'il faut - Gestion des versions : Spring Boot aligne les versions des bibliothèques entre elles pour toi
Spring Boot 3 = Java 17 minimum
jakarta. et non plus javax.). Dans ce cours on part sur JDK 21, la version LTS actuelle.Pour aller plus loin
- Spring Initializr · spring.io (en anglais) (nouvel onglet)
- Building a RESTful Web Service (guide officiel) · spring.io (en anglais) (nouvel onglet)
- Accessing Data with JPA (guide officiel) · spring.io (en anglais) (nouvel onglet)
- Spring Boot Reference Documentation · docs.spring.io (en anglais) (nouvel onglet)
Chapitre 2
Créer et lancer un projet
On ne crée pas un projet Spring Boot à la main : on utilise Spring Initializr, qui génère la structure et le build.
Voici la première bonne surprise pour qui vient d'autres écosystèmes : on ne crée jamais un projet Spring Boot à la main, en assemblant péniblement des fichiers de configuration. On utilise Spring Initializr, un générateur officiel qui produit toute la structure du projet et son fichier de build prêts à l'emploi. Accessible sur start.spring.io, il est aussi intégré directement dans IntelliJ IDEA et dans l'extension Spring de VS Code — tu n'as donc même pas à quitter ton éditeur.
Au moment de la génération, tu fais quelques choix : l'outil de build (Maven, le plus répandu), le langage (Java), la version de Spring Boot et de Java (21), et surtout les dépendances de départ. Pour une API qui persiste des données, quatre suffisent : Spring Web (exposer une API REST), Spring Data JPA (la persistance), H2 Database (une base en mémoire idéale pour apprendre, qui disparaît à l'arrêt) et Validation (valider les données entrantes). Tu ajouteras d'autres starters plus tard selon les besoins, mais ce socle couvre tout ce dont ce cours a besoin.
Une fois le projet ouvert, une commande le lance, et un détail mérite d'être souligné : le wrapper Maven. Les fichiers mvnw (et mvnw.cmd sous Windows) présents à la racine téléchargent automatiquement la bonne version de Maven — tu n'as donc rien à installer, et toute l'équipe utilise exactement la même version de l'outil de build, ce qui élimine les « ça marche chez moi » liés aux versions. Un ./mvnw spring-boot:run démarre l'application sur http://localhost:8080, serveur embarqué compris. En moins de deux minutes, tu as une API Java qui tourne.
Spring Initializr
Rends-toi sur start.spring.io (intégré dans IntelliJ et dans l'extension VS Code « Spring Boot »). Choisis : projet Maven, langage Java, dernière version stable de Spring Boot, packaging Jar, Java 21.
Les dépendances de départ
- Spring Web : pour exposer une API REST
- Spring Data JPA : pour la persistance en base
- H2 Database : une base en mémoire, parfaite pour apprendre
- Validation : pour valider les données entrantes
Lancer l'application
# Avec le wrapper Maven fourni (pas besoin de Maven installé)
./mvnw spring-boot:run # Linux / macOS
mvnw.cmd spring-boot:run # Windows
# L'application démarre sur http://localhost:8080Le wrapper Maven (mvnw)
mvnw (et mvnw.cmd sous Windows) télécharge la bonne version de Maven automatiquement. Tu n'as rien à installer : tout le monde dans l'équipe utilise la même version.Chapitre 3
Structure d'un projet Spring Boot
Avant d'écrire du code, comprenons les fichiers clés générés par Spring Initializr.
Avant d'écrire du code, prends quelques minutes pour comprendre ce que Spring Initializr a généré. Un projet Spring Boot suit des conventions bien établies, et les connaître t'évite de chercher où poser tes fichiers ou pourquoi une classe « n'est pas prise en compte ». Deux éléments méritent une attention particulière dès le départ : la classe principale et le fichier de configuration.
La classe principale est le point d'entrée de l'application, marquée par l'annotation @SpringBootApplication. Cette annotation n'est pas anodine : elle en combine trois à elle seule. @Configuration (la classe peut définir de la configuration), @EnableAutoConfiguration (elle active la fameuse configuration automatique) et @ComponentScan (elle demande à Spring de parcourir le projet pour y détecter les classes à gérer). Cette dernière a une conséquence pratique importante : toutes tes classes doivent se trouver sous le package de la classe principale, sinon Spring ne les verra pas — une source d'erreur classique chez les débutant·es.
Le second élément clé est le fichier application.properties (ou sa variante .yml), qui centralise toute la configuration : le port du serveur, la connexion à la base de données, les niveaux de log. Le regrouper à un seul endroit rend l'application facile à reconfigurer sans toucher au code. Enfin, une convention d'organisation largement adoptée consiste à créer un package par couche — controller, service, repository, model — un découpage que ce cours détaille et qui structure proprement même les gros projets.
La classe principale
package com.exemple.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}L'annotation @SpringBootApplication est le point d'entrée. Elle combine trois annotations : @Configuration (config), @EnableAutoConfiguration (config automatique) et @ComponentScan (détection des classes du projet).
Le fichier de configuration
src/main/resources/application.properties (ou .yml) centralise la config : port, base de données, niveaux de logs…
# Port du serveur (8080 par défaut)
server.port=8080
# Base H2 en mémoire
spring.datasource.url=jdbc:h2:mem:demo
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
# Console H2 accessible sur /h2-console
spring.h2.console.enabled=true
# Affiche le SQL généré par JPA dans les logs
spring.jpa.show-sql=trueOrganiser ses packages
controller, service, repository, model). Garde toutes tes classes sous le package de la classe principale, sinon le @ComponentScan ne les voit pas.Chapitre 4
Conteneur IoC et injection de dépendances
C'est le cœur de Spring. Plutôt que de créer tes objets avec « new », tu laisses Spring les construire et te les fournir.
On arrive au cœur de Spring, le concept qui explique pourquoi le framework existe : l'inversion de contrôle (IoC) et l'injection de dépendances. L'idée peut dérouter au début, mais elle est libératrice une fois comprise. Normalement, quand une classe A a besoin d'une classe B, elle l'instancie elle-même avec new B(). Le problème : A est alors couplée à une implémentation précise de B, difficile à remplacer et à tester. L'inversion de contrôle renverse cette responsabilité — d'où son nom.
Concrètement, Spring maintient un conteneur : un annuaire d'objets qu'il gère lui-même, appelés beans. Tu te contentes de déclarer qu'une classe est un bean, et Spring s'occupe de l'instancier une fois et de la fournir (l'injecter) partout où on en a besoin. Tu n'écris plus new pour tes composants : tu demandes une dépendance, Spring te la donne. Pour déclarer un bean, on utilise des annotations qui expriment aussi son rôle : @Component (générique), @Service (logique métier), @Repository (accès aux données) ou @RestController (exposition d'API). Ces variantes font toutes la même chose techniquement, mais rendent l'intention lisible.
Un point de pratique important, souvent mal enseigné : privilégie l'injection par constructeur, comme dans l'exemple ci-dessous, plutôt que d'annoter directement un champ avec @Autowired. L'injection par constructeur rend les dépendances explicites (on voit d'un coup d'œil de quoi la classe a besoin), permet de marquer les champs final (immuables, donc plus sûrs), et surtout facilite grandement les tests : tu peux passer un objet fictif (mock) directement au constructeur. L'injection sur le champ, elle, masque les dépendances et complique les tests unitaires. C'est le genre de bonne pratique qui distingue un code Spring d'amateur d'un code professionnel.
Spring maintient un conteneur IoC (Inversion of Control) : un annuaire d'objets gérés, appelés beans. Tu déclares qu'une classe est un bean, et Spring s'occupe de l'instancier et de l'injecter là où on en a besoin. C'est l'injection de dépendances.
Déclarer un bean
@Component: bean générique@Service: bean de logique métier (sémantique plus claire)@Repository: bean d'accès aux données@RestController: bean qui expose des endpoints HTTP
Injecter par le constructeur (recommandé)
@Service
public class TaskService {
private final TaskRepository repository;
// Injection par constructeur : Spring fournit le repository
public TaskService(TaskRepository repository) {
this.repository = repository;
}
public List<Task> findAll() {
return repository.findAll();
}
}Constructeur plutôt que @Autowired sur le champ
final (immuables) et facilite les tests. Évite @Autowired directement sur les attributs : c'est plus difficile à tester et masque les dépendances.Vrai ou faux ?
Mettre @Autowired sur le champ est la meilleure pratique d'injection en Spring.
Chapitre 5
Exposer une API REST
On expose maintenant des endpoints HTTP avec un contrôleur REST.
C'est le moment où ton application devient utile : exposer une API REST, c'est-à-dire des points d'accès (endpoints) HTTP que d'autres programmes — un front, une application mobile, un autre service — peuvent appeler. En Spring, cela se fait avec un contrôleur REST, une classe annotée @RestController dont chaque méthode répond à une requête HTTP et renvoie du JSON.
Le contrôleur repose sur un petit vocabulaire d'annotations qu'il faut connaître, illustré dans l'exemple ci-dessous. @RestController combine @Controller et @ResponseBody, ce qui sérialise automatiquement chaque retour en JSON. @RequestMapping("/api/tasks") pose un préfixe d'URL commun à toute la classe. Puis, sur chaque méthode, une annotation par verbe HTTP : @GetMapping pour lire, @PostMapping pour créer, @PutMapping pour modifier, @DeleteMapping pour supprimer. Enfin, on récupère les données de la requête avec @PathVariable (un segment d'URL comme /{id}), @RequestBody (le corps JSON désérialisé en objet Java) ou @RequestParam (un paramètre comme ?page=2).
Un principe doit guider l'écriture de tes contrôleurs : ils restent minces. Le rôle d'un contrôleur est de traduire entre le monde HTTP et le monde Java, puis de déléguer le vrai travail à un service — jamais de contenir la logique métier lui-même (c'est l'objet du chapitre suivant sur les couches). Pour maîtriser finement la réponse (un statut 201 avec un en-tête Location, un 404 propre), tu peux retourner un ResponseEntity plutôt que directement l'objet. Et pour gérer les erreurs sans alourdir chaque méthode, mieux vaut lever une exception métier et la centraliser, une technique détaillée au dernier chapitre. Un contrôleur bien écrit se lit en dix secondes.
Un contrôleur minimal
@RestController
@RequestMapping("/api/tasks")
public class TaskController {
private final TaskService service;
public TaskController(TaskService service) {
this.service = service;
}
@GetMapping
public List<Task> getAll() {
return service.findAll();
}
@GetMapping("/{id}")
public Task getOne(@PathVariable Long id) {
return service.findById(id);
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Task create(@RequestBody Task task) {
return service.create(task);
}
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(@PathVariable Long id) {
service.delete(id);
}
}Les annotations à retenir
@RestController: combine@Controlleret@ResponseBody(les retours sont sérialisés en JSON)@RequestMapping("/api/tasks"): préfixe d'URL commun au contrôleur@GetMapping,@PostMapping,@PutMapping,@DeleteMapping: un par verbe HTTP@PathVariable: récupère un segment d'URL (/{id})@RequestBody: désérialise le corps JSON de la requête en objet Java@RequestParam: récupère un paramètre de requête (?page=2)
ResponseEntity pour maîtriser la réponse
ResponseEntity<T> : par ex. return ResponseEntity.status(HttpStatus.CREATED).body(task);. Pratique pour renvoyer un 404 ou un 201 avec en-tête Location.@GetMapping, @PostMapping, etc. sont des raccourcis de @RequestMapping spécialisés par verbe HTTP. On met @RequestMapping sur la classe pour le préfixe d'URL commun, puis les annotations par verbe sur chaque méthode. Plus lisible que de répéter @RequestMapping(method = ...).TaskNotFoundException) et la mapper vers un 404 dans un @RestControllerAdvice, ou retourner un ResponseEntity.notFound().build() depuis le contrôleur. La première approche garde le contrôleur léger et centralise les statuts d'erreur.@Controller sert à rendre des vues (pages HTML, templates Thymeleaf). @RestController = @Controller + @ResponseBody : chaque retour est sérialisé en JSON. Pour une API REST, c'est toujours @RestController.Chapitre 6
Découper en couches : modèle, service, repository
Une bonne API sépare les responsabilités. Le contrôleur reçoit, le service décide, le repository persiste.
À mesure qu'une application grossit, entasser toute la logique dans les contrôleurs mène au chaos. La solution universelle, en Spring comme ailleurs, est l'architecture en couches : découper le code selon les responsabilités, chaque couche ayant un rôle unique et clair. Ce n'est pas de la bureaucratie, c'est ce qui permet de faire évoluer, tester et comprendre un projet sur la durée.
On distingue classiquement quatre couches, avec un flux bien défini de l'une à l'autre. Le contrôleur reçoit la requête HTTP et la traduit, sans jamais contenir de logique métier. Le service porte cette logique métier : les règles, les validations, l'orchestration entre plusieurs opérations. Le repository s'occupe uniquement de l'accès aux données : lire, écrire, interroger la base. Et le modèle (ou entité) représente les objets manipulés. Une image simple résume le flux : le contrôleur reçoit, le service décide, le repository persiste. Chacun ignore les détails des autres.
Ce découpage a des bénéfices très concrets. Tu peux tester la logique métier du service sans lancer de serveur HTTP ni de base de données, en lui fournissant un faux repository. Tu peux changer de base de données sans toucher au service. Et n'importe qui rejoignant le projet sait immédiatement où chercher : un bug de règle métier est dans le service, un problème de requête dans le repository. Une bonne pratique complémentaire, signalée dans le cours : en production, on n'expose pas directement l'entité de base de données via l'API, mais un DTO — un objet dédié au transfert — afin de découpler l'API de la base et de ne pas divulguer de champs internes.
Le rôle de chaque couche
- Controller : traduit HTTP ↔ Java. Ne contient pas de logique métier
- Service : la logique métier (règles, validations, orchestration)
- Repository : l'accès aux données (requêtes, persistance)
- Model / Entity : les objets manipulés (ce qui est stocké)
Le service
@Service
public class TaskService {
private final TaskRepository repository;
public TaskService(TaskRepository repository) {
this.repository = repository;
}
public List<Task> findAll() {
return repository.findAll();
}
public Task findById(Long id) {
return repository.findById(id)
.orElseThrow(() -> new TaskNotFoundException(id));
}
public Task create(Task task) {
return repository.save(task);
}
public void delete(Long id) {
repository.deleteById(id);
}
}DTO : ne jamais exposer ton entité directement
Chapitre 7
Persister avec Spring Data JPA
Spring Data JPA te génère les requêtes SQL courantes à partir d'une simple interface. Tu écris l'entité, l'interface, et c'est tout.
Persister des données — les enregistrer durablement en base — est le besoin de presque toute application. En Java, la norme historique s'appelle JPA, et Spring Data JPA la rend étonnamment simple. Sa promesse : tu écris ton entité et une simple interface, et Spring génère pour toi tout le code d'accès aux données. Tu n'écris presque aucune requête SQL à la main. C'est une magie qui déroute au début tant elle fait de choses toute seule.
Deux éléments suffisent. L'entité est une classe Java annotée @Entity dont chaque champ correspond à une colonne : l'annotation @Id désigne la clé primaire, @GeneratedValue demande à la base de la générer. Le repository est une interface qui hérite de JpaRepository<Task, Long> — et là est la magie : par ce simple héritage, tu obtiens gratuitement tout le CRUD de base (findAll, findById, save, deleteById), plus la pagination et le tri. Tu n'as rien à implémenter ; Spring génère l'implémentation au démarrage.
Encore plus surprenant : Spring Data génère aussi des requêtes personnalisées à partir du nom des méthodes. Déclarer List<Task> findByDone(boolean done) dans l'interface suffit pour que Spring comprenne « cherche les tâches où done vaut la valeur donnée » et écrive le SQL correspondant. C'est puissant et lisible. Un avertissement de production toutefois : le réglage ddl-auto=update, qui laisse Hibernate créer et modifier les tables automatiquement, est très pratique en développement mais dangereux en production — un schéma réel se gère avec des migrations versionnées (Flyway, Liquibase) pour garder le contrôle et l'historique des changements.
L'entité
import jakarta.persistence.*;
@Entity
public class Task {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private boolean done;
// Constructeur vide requis par JPA
public Task() {}
// getters / setters...
}Le repository
import org.springframework.data.jpa.repository.JpaRepository;
public interface TaskRepository extends JpaRepository<Task, Long> {
// Méthodes générées automatiquement à partir du nom :
List<Task> findByDone(boolean done);
List<Task> findByTitleContainingIgnoreCase(String fragment);
}En héritant de JpaRepository<Task, Long>, tu obtiens gratuitement findAll(), findById(), save(), deleteById(), la pagination, le tri… Et Spring génère les requêtes à partir du nom des méthodes (findByDone → WHERE done = ?).
ddl-auto en développement seulement
spring.jpa.hibernate.ddl-auto=update laisse Hibernate créer/modifier les tables automatiquement : pratique en dev, dangereux en production. En prod, gère le schéma avec des migrations versionnées (Flyway ou Liquibase).Vrai ou faux ?
En héritant de JpaRepository<Task, Long>, tu obtiens findAll(), findById(), save() et deleteById() sans écrire leur code.
Chapitre 8
Validation et gestion des erreurs
Une API robuste refuse les données invalides proprement et renvoie des erreurs claires plutôt qu'une stack trace.
Une API amateur fait confiance aux données qu'elle reçoit ; une API robuste ne fait confiance à rien. Ce dernier chapitre traite deux aspects qui séparent une API qui « marche en démo » d'une API prête pour la production : refuser proprement les données invalides, et renvoyer des erreurs claires plutôt qu'une vilaine trace d'exception qui, en plus d'être illisible, peut divulguer des détails internes à un attaquant.
Pour la validation, Spring s'appuie sur des annotations posées directement sur les champs de l'entité ou du DTO : @NotBlank pour un champ obligatoire, @Size pour une longueur maximale, et bien d'autres (@Email, @Min, @Pattern). Ces contraintes ne se déclenchent que si tu les actives explicitement, en ajoutant @Valid devant le @RequestBody du contrôleur. Dès lors, toute requête contenant des données non conformes est automatiquement rejetée avant même d'atteindre ta logique métier — tu déclares les règles, Spring les fait respecter.
Pour la gestion des erreurs, l'outil clé est @RestControllerAdvice : une classe qui centralise le traitement des exceptions de toute l'application. À l'intérieur, chaque méthode @ExceptionHandler associe un type d'exception à un statut HTTP et à un corps de réponse propre. Ainsi, une TaskNotFoundException levée n'importe où dans le service devient automatiquement une belle réponse 404 en JSON, et une erreur de validation un 400 explicite listant les champs fautifs. L'avantage est double : tes contrôleurs restent légers (ils lèvent des exceptions et ne s'occupent pas de les traduire), et le comportement d'erreur est cohérent sur toute l'API. C'est cette centralisation qui donne à une API Spring son allure professionnelle et prévisible.
Valider les entrées
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
public class Task {
@NotBlank(message = "Le titre est obligatoire")
@Size(max = 120, message = "120 caractères maximum")
private String title;
// ...
}@PostMapping
public Task create(@Valid @RequestBody Task task) {
return service.create(task);
}Centraliser la gestion d'erreurs
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(TaskNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public Map<String, String> handleNotFound(TaskNotFoundException ex) {
return Map.of("error", ex.getMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> handleValidation(MethodArgumentNotValidException ex) {
String message = ex.getBindingResult().getFieldErrors().stream()
.map(e -> e.getField() + " : " + e.getDefaultMessage())
.collect(Collectors.joining(", "));
return Map.of("error", message);
}
}Tu as une API complète
🧭 Diagnostiquer une erreur 500 sur un POST
Ton endpoint <code>POST /api/tasks</code> renvoie un <strong>500 Internal Server Error</strong>. Le front affiche juste « erreur serveur ». Par où commences-tu ?
Premier réflexe ?
🛠️ Exercice optionnel
Construire l'API REST d'une todo-list
Tu pars d'un projet Spring Initializr vide (Spring Web + Spring Data JPA + H2 + Validation). Objectif : une API CRUD complète pour gérer des tâches, proprement découpée en couches.
Ta mission
- Entité : créer
Taskavecid,title(non vide, max 120) etdone. - Repository :
TaskRepository extends JpaRepository<Task, Long>+ une méthodefindByDone(boolean). - Service :
TaskServiceavec findAll, findById (404 si absent), create, delete, injecté par constructeur. - Contrôleur :
TaskControllerexposantGET /api/tasks,GET /api/tasks/{id},POST /api/tasks(201),DELETE /api/tasks/{id}(204). - Validation :
@Validsur le POST, refuser un titre vide avec un 400. - Erreurs : un
@RestControllerAdvicequi renvoie 404 pour une tâche introuvable. - Test manuel : créer 2 tâches en POST, les lister, en supprimer une, vérifier la liste.
Critère de réussite : un POST avec un titre vide renvoie un 400 lisible ; un GET sur un id inexistant renvoie un 404 ; le CRUD complet fonctionne.
Tu bloques ? Des indices, à dévoiler quand tu en as besoin.
Indice 1
Indice masqué.
Indice 2
Indice masqué.
Indice 3
Indice masqué.
✅ QCM de fin de cours
Teste tes acquis
10 questions, plusieurs réponses parfois possibles. Coche tout ce qui te semble juste, puis valide pour voir ton score et les explications.
- 1
Qu'apporte Spring Boot par rapport à Spring « classique » ?
- 2
Que combine l'annotation
@SpringBootApplication? - 3
Quelle est la façon recommandée d'injecter une dépendance ?
- 4
Quelle annotation expose une classe comme contrôleur REST renvoyant du JSON ?
- 5
Comment récupérer le corps JSON d'une requête POST dans un paramètre ?
- 6
En héritant de
JpaRepository<Task, Long>, qu'obtiens-tu gratuitement ? - 7
Quelle annotation déclenche la validation d'un objet reçu dans un contrôleur ?
- 8
Comment centraliser la gestion des erreurs d'une API Spring Boot ?
- 9
Dans une architecture en couches, où doit vivre la logique métier d'une API Spring Boot ?
- 10
Comment renvoyer un statut
201 Createdavec le corps de l'objet créé depuis un contrôleur ?
Tu peux laisser des questions sans réponse, elles compteront comme fausses.
🎓 Attestation
Ton attestation de réussite
Termine le QCM avec au moins 80% de bonnes réponses pour débloquer ton attestation de réussite.
Tu veux ce cours pour ton équipe ?
Je peux adapter et animer ce cours pour tes formateur·ices ou tes apprenant·es, en présentiel ou en distanciel. Parlons-en pendant l'audit gratuit.
Réserver un audit gratuit →