TP Spotify Rest
Table des matières
Objectifs
L'objectif de ce TP est de modifier le projet Spotify précédent pour qu'il ne soit plus qu'un backend REST.
Puis nous allons intégrer une couche de sécurité en employant un JWT Token.
Création du projet Spotify Rest
Préparation
Dans C:\Academie\, créer un répertoire spotify-rest et copier à l'intérieur toutes les sources du projet spotify-web
La structure devrait être :
|-Academie
|- spotify-rest
|- spotify-webCréation du projet dans IntelliJ CE
Ouvrir le répertoire précédemment créér : par exemple C:/Academie/spotify-rest

IntelliJ devrait alors reconnaitre la structure et proposer de charger Gradle.

Une fois les tasks Gradle disponibles, lancer :

L'application est disponible sur : http://localhost:8080
Processus d'adaptation
Supprimer les vues GSP
Les fichiers .gsp dans /views ne sont plus nécessaires.
Effacer le répertoire `views
Ajouter les dépendances nécessaires
Dans build.gradle, ajouter ces dépendances :
// JSON processing
implementation "org.grails.plugins:views-json"
implementation "org.grails.plugins:views-json-templates"Adaptation de la configuration
Adapter le fichier application.yaml comme suit :
grails:
mime:
types:
json:
- application/json
- text/json
controllers:
defaultScope: singleton
views:
json:
generator:
dateFormat: "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
pretty: trueAdaptation du mapping
Adapter le fichier UrlMapping.groovy comme suit :
class UrlMappings {
static mappings = {
// API mappings
"/api/$controller/$action?/$id?(.$format)?" {
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
"404"(view:'/notFound')
}
}Création d'un trait pour gérer les réponses
Définition
Un trait en Grails (et plus généralement en Groovy) est un mécanisme de réutilisation de code qui permet de partager des comportements entre plusieurs classes sans utiliser l'héritage. Les traits ont été introduits dans Groovy 2.3 et sont similaires aux interfaces en Java 8 avec des méthodes par défaut, mais avec plus de fonctionnalités.
Voici les caractéristiques principales des traits en Grails :
Réutilisation de code horizontal :
Les traits permettent d'ajouter des fonctionnalités à plusieurs classes sans créer une hiérarchie d'héritage complexe.Implémentation de comportements :
Contrairement aux interfaces Java traditionnelles, les traits peuvent contenir des implémentations de méthodes.Multiple traits : Une classe peut implémenter plusieurs traits, ce qui permet de composer des fonctionnalités.
État et comportement : Les traits peuvent définir à la fois des propriétés (état) et des méthodes (comportement).
Surcharge de méthodes : Les classes qui implémentent un trait peuvent surcharger les méthodes du trait.
Dans note application nous allons créer un trait ResponseHandler pour partager un comportement de gestion de réponses HTTP entre plusieurs contrôleurs.
Cela permet d'avoir un code cohérent pour générer des réponses JSON dans tous les contrôleurs sans dupliquer le code ni créer une hiérarchie d'héritage.
Dans src/main/groovy/com/talent, créer le trait comme suit :
package com.talent
import grails.converters.JSON
import org.springframework.http.HttpStatus
trait ResponseHandler {
/**
* Envoie une réponse HTTP avec le statut et les données spécifiés
* @param status le statut HTTP
* @param data les données à envoyer
*/
void sendHttpResponse(HttpStatus status, JSON data) {
render(status: status, data)
}
void sendSuccess(Object data) {
sendHttpResponse (HttpStatus.OK, data as JSON)
}
void sendError(Exception exception) {
sendHttpResponse(HttpStatus.INTERNAL_SERVER_ERROR, [message : exception.message] as JSON)
}
void sendError(String errorMessage) {
sendHttpResponse(HttpStatus.INTERNAL_SERVER_ERROR, [message : errorMessage] as JSON)
}
void sendNotFound(String message) {
sendHttpResponse(HttpStatus.NOT_FOUND, [message : message] as JSON)
}
}Adaptation du contrôleur AlbumController
Intégrons maintenant le trait dans le contrôleur Album, et adaptons les méthodes.
package com.talent
import grails.validation.ValidationException
import static org.springframework.http.HttpStatus.*
class AlbumController implements ResponseHandler {
AlbumService albumService
def index = {
params.max = Math.min(params.max ?: 10, 100)
def albums = albumService.list(params)
def count = albumService.count()
def result = [
albums: albums,
total: count,
page: params.offset ? (params.offset.toInteger() / params.max.toInteger()) + 1 : 1,
pages: Math.ceil(count / params.max)
]
sendSuccess(result)
}
def show = {
def album = albumService.get(params.id as Long)
if (!album) {
sendNotFound("Album not found with ID ${params.id}")
return
}
sendSuccess(album)
}
def save() {
def album = new Album(request.JSON)
if (!album.validate()) {
sendErrorResponse(BAD_REQUEST, album.errors.allErrors.collect { it.defaultMessage }.join(", "))
return
}
try {
album = albumService.save(album)
sendSuccess(album)
} catch (ValidationException e) {
sendError(e)
}
}
def update = {
def album = albumService.get(params.id as Long)
if (!album) {
sendNotFound("Album not found with ID ${id}")
return
}
album.properties = request.JSON
if (!album.validate()) {
sendError(album.errors.allErrors.collect { it.defaultMessage }.join(", "))
return
}
try {
album = albumService.save(album)
sendSuccess(album)
} catch (ValidationException e) {
sendError(e)
}
}
def delete = {
def album = albumService.get(params.id)
if (!album) {
sendErrorResponse(NOT_FOUND, "Album not found with ID ${id}")
return
}
try {
albumService.delete(id)
sendSuccess([message: "Album with ID ${id} deleted successfully"])
} catch (Exception e) {
sendError(e)
}
}
}Adaptation des contrôleurs ArtistController et SongController
Sur le même principe, adapter les 2 autres controlleurs.
Tests avec Postman
Créez des requêtes pour tester les différents endpoints :
- GET albums
Method: GET
URL: http://localhost:8080/api/album/index
Data:
GET album by ID
Method: GET
URL:
http://localhost:8080/api/album/showData:
POST new artist (admin only)
- URL:
http://localhost:8080/artists - Headers:
Authorization: Bearer {{authToken}} - Body (raw, JSON):
{ "name": "Queen", "biography": "Groupe de rock britannique formé à Londres en 1970.", "imageUrl": "https://example.com/queen.jpg" }- URL:
PUT update artist (admin only)
- URL:
http://localhost:8080/artists/3 - Headers:
Authorization: Bearer {{authToken}} - Body (raw, JSON):
{ "name": "Queen", "biography": "Groupe de rock britannique légendaire formé à Londres en 1970 par Freddie Mercury, Brian May, Roger Taylor et John Deacon.", "imageUrl": "https://example.com/queen_updated.jpg" }- URL:
GET albums by artist
- URL:
http://localhost:8080/artists/1/albums - Headers:
Authorization: Bearer {{authToken}}
- URL:
GET songs by album
- URL:
http://localhost:8080/albums/1/songs - Headers:
Authorization: Bearer {{authToken}}
- URL: