Bonnes pratiques pour le controller
A lire
https://stackoverflow.blog/2020/03/02/best-practices-for-rest-api-design/
Séparation des taches
Le controller est le point d’accès au backend. Son but est :
- d’aiguiller les requetes des clients vers les bons traitements
- de renvoyer des réponses avec des httpStatus adéquats
Pour simplifier la maintenance du projet, il est important de bien séparer les taches, notamment les méthodes du controller doivent transmettre à des services toute la logique métier.
Par exemple lorsqu’on ajoute un nouvel objet en base et qu’il faut faire des contrôles avant de faire cet ajout (par exemple pour vérifier que des critères sont remplis), ces contrôles (la logique métier) doit se faire dans des services à part.
C’est aussi un pas vers junit, car on sait exactement ce que doit faire le controller et ce que l’on doit tester. Pour reprendre l’exemple, on ne teste pas les critères de contrôle qui sont de la logique métier (donc testée dans le service).
Simplification des endpoints
Une bonne pratique est d’utiliser un minimum de endpoints différents. Pour cela on utilise des verbes http pour distinguer les requetes.
- Un préfixe pour toute la classe (en général le nom de l’entité au pluriel)
@RequestMapping(path = "/heroes") public class HeroController { ... }
- Pas d’adresse pour le getAll, il suffit de faire un get sur /heroes
@GetMapping public @ResponseBody Iterable<Hero> getAll() { return heroRepository.findAll(); }
- Si on fait un post sur /heroes, on tombera dans cette méthode, pas besoin de définir une nouvelle adresse
@PostMapping public @ResponseBody Hero addNew(@RequestBody Hero newHero) { return heroRepository.save(newHero); }
Réponses du controller
Une solution simple pour envoyer des réponses bien formatées est que les signatures des méthodes du controller renvoient directement les objets demandés. Sping va ensuite se charger de wrapper la réponse dans un json :
- Ici, la méthode retourne un
Auteur
:@GetMapping(path = "/{auteurID}") public @ResponseBody Auteur getOne(@PathVariable long auteurID) throws AuteurNotFoundException { if (auteurRepository.existsById(auteurID)) { return auteurRepository.findById(auteurID).get(); } else { throw new AuteurNotFoundException(); } }
- Pour envoyer le bon httpStatus, on peut ajouter
@ResponseStatus
:@PostMapping @ResponseStatus(value= HttpStatus.CREATED, reason = "Objet bien crée") public Auteur create() { ... }
- Ou en cas d’erreur, par exemple dans l’exception de l’exemple du dessus :
@ResponseStatus(value= HttpStatus.NOT_FOUND, reason = "cet auteur not found") public class AuteurNotFoundException extends Exception { }
Exemples
https://github.com/jtobelem-simplon/hibernate-many-to-many
https://github.com/jtobelem-simplon/toh-backend-tests
@author Josselin Tobelem