Cours 3 - Requêtes HTTP
✅ Une application Web client Angular peut :
- Gérer tout le côté visuel du site Web.
- Gérer le routage (illusion de « changement de page »)
- Rendre une page Web dynamique à l'aide de JavaScript.
- Gérer l'internationalisation / i18n / la traduction.
- Etc. je suis ne suis pas pour tout écrire 😠
🛑 Une application Web cliente Angular ne doit pas :
- Se connecter directement à une base de données pour obtenir les données à afficher. (Vidéos 📽, images 🖼, messages 📃, etc)
Puisque l'application Angular est exécutée sur l'ordinateur du client (de l'utilisateur), tout le code HTML, CSS et JavaScript est accessible par nos utilisateurs. 🙈😩 Donc si on inclut des identifiants pour se connecter à une base de données... la sécurité de notre base de données sera compromise.

Oui mais si on utilise seulement des identifiants permettant de faire des SELECT sur certaines tables précises ?
À un certain moment, il faudra faire de la gestion utilisateurs, permettre d'ajouter du contenu sur le site Web (commentaires, articles, vidéos, images, etc... donc des INSERT dans la base de données !), etc. Donc il va falloir qu'une autre application (qui n'est pas un projet Angular) s'en occupe.
À partir de la semaine 8, nous créerons un serveur Web ASP.NET Core nous-mêmes, mais pour le moment, nous allons envoyer des requêtes à des serveurs Web existants pour obtenir des données à afficher dans nos applications Web.
Beaucoup de d'API Web (serveurs Web auxquel on peut envoyer des requêtes) sont payants 💲, mais il en existe de nombreux qui sont gratuits 🗿 ou partiellement gratuits et auxquels nous pourrons envoyer des requêtes HTTP en échange de données à des fins d'apprentissage.
🌐 Exemple d'API Web
Last FM est une webradio et un site Web qui propose des données en lien avec la musique.
Last FM met à notre disposition une API (un serveur Web avec lequel nous pourrons interagir) auquel on peut envoyer des requêtes HTTP pour obtenir des données sour format JSON ou XML pour ensuite utiliser ces données pour meubler les pages Web de notre application Angular.

Rendez vous à cette page pour accéder à la documentation qui décrit toutes les requêtes disponibles avec Last FM.

Par exemple, si je souhaite obtenir des informations sur un album en particulier, la requête nommée album.getInfo
risque
de répondre à mes besoins.

Last FM nous fournit un exemple d'URL complète pour cette requête :
http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=YOUR_API_KEY&artist=Cher&album=Believe&format=json
On voit que l'artiste utilisé dans l'exemple fourni est Cher
et l'album est Believe
.
La seule chose qu'il resterait à faire pour que cette requête soit fonctionnelle, c'est de remplacer YOUR_API_KEY
par
une clé d'API. Il est possible d'en obtenir une en créant un compte Last FM, mais je vous en fournis une car vous êtes paresseux
pour vous simplifier la vie.
🔑 Clé d'API : 9a8a3facebbccaf363bb9fd68fa37abf
On peut même essayer la requête directement dans la barre d'adresse d'un navigateur (L'affichage avec Firefox 🔥🦊 est le plus clair) :

Ce qu'on voit présentement est un objet JSON. Nous allons pouvoir extraire des données (liste de chansons, genre, nom de l'album, etc.) de notre choix afin de les afficher dans notre application Angular. Tout ceci sera automatisé avec du code TypeScript dans notre application.
🏗 Prérequis pour faire des requêtes
Dans Angular, il existe une classe nommée HttpClient
qui simplifie l'envoi de requêtes HTTP. Pour y avoir accès dans
un composant, voici les étapes à suivre :
🔨 1 - Modifier app.config.ts
Dans le fichier ⚙ app.config.ts
, qui est un fichier de configuration ayant un impact sur tous les composants de l'application
Angular, il faudra ajouter une instruction qui donnera accès à la classe HttpClient au reste du projet :
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideHttpClient() // Cette ligne doit être ajoutée
]
};
💉 2 - Injecter HttpClient dans le composant
export class AppComponent {
constructor(public http : HttpClient){}
}
C'est aussi simple que cela. L'ajout de HttpClient
dans le constructeur permettra à Angular d'automatiquement
fournir un objet de type HttpClient
lorsque le composant app
sera instancié au chargement de la page Web. (Ce
qui correspond à une injection de dépendance 💉)
Dit autrement : « quand la page Web va ouvrir, Angular va, en cachette, faire un new AppComponent(...)
et lui
fournira un new HttpClient()
dans son constructeur.
Comme on a vu, le mot-clé public
, pour le paramètre public http : HttpClient
, va permettre d'automatiquement
créer une variable de classe nommée http
dans le composant app
.
✈ Envoyer une requête
Si on utilise l'URL de la requête qui a été abordé un peu plus haut, ça pourrait ressembler à ceci dans la classe
de notre composant app
:
async getSongs(){
let x = await lastValueFrom(this.http.get<any>("http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=9a8a3facebbccaf363bb9fd68fa37abf&artist=Cher&album=Believe&format=json"));
console.log(x);
}
🔍 Avant de jeter un coup d'oeil à ce que console.log(x)
a imprimé dans la console du navigateur, abordons quelques
éléments clés de cette fonction :
- On voit que l'URL de la requête (
"http://ws.audioscrobbler....."
) a été glissé dans la fonctionthis.http.get()
. C'est une fonction qui est accessible grâce à l'objetHttpClient
qui a été injecté dans le constructeur. this.http.get()
permet d'envoyer des requêtes HTTP de typeGET
. (Exemples d'autres types de requête :post
,put
,delete
, etc.)- On remarque les éléments
<any>
,async
,await
etlastValueFrom()
, qui seront expliqués en détails plus loin.
Dans la console du navigateur où la fonction getSongs()
a été appelée, on peut apercevoir ceci suite à l'appel de
console.log(x)
:

C'est le même objet JSON que lorsque nous avions directement testé la requête dans la barre d'adresse du navigateur.
Cependant, cette fois-ci, l'objet JSON a été stocké dans la variable x
! On pourrait donc accéder à toutes les données
de l'objet JSON en manipulant la variable x
qui a été déclarée dans la fonction getSongs()
.
📝 Extraire des données de l'objet JSON
Disons qu'on souhaite afficher le nom de l'artiste et le titre de l'album :
Voici comment on pourrait extraire ces données dans le code :
async getSongs(){
let x = await lastValueFrom(this.http.get<any>("http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=9a8a3facebbccaf363bb9fd68fa37abf&artist=Cher&album=Believe&format=json"));
console.log(x);
let nomArtiste : string = x.album.artist; // Contient "Cher"
let nomAlbum : string = x.album.name; // Contient "Believe"
}
Pour déterminer le chemin (Exemple : x.album.artist
) vers une donnée à extraire, il faut partir de la racine
de l'objet JSON et descendre dans sa hiérarchie jusqu'à la propriété voulue.

Pourquoi le chemin utilisé dans le code n'est pas
x.Object.album.artist
?
Comme nous avons rangé l'objet JSON dans une variable nommée x
dans le code, et que Object
n'est qu'un
placeholder pour représenter la racine de l'objet JSON, on doit simplement utiliser x
lorsqu'on parle de la racine.
Notez que sans le <any>
placé dans la fonction this.http.get<any>(..)
, nous n'aurons pas pu utiliser le chemin
x.quelqueChose.quelqueChose...
. Sans le <any>
, TypeScript ne nous aurons pas laissé accéder aux propriétés de
l'objet JSON dans la variable x
. En utilisant <any>
, on dit à TypeScript que la variable x
pourrait être
n'importe quoi et le compilateur nous donne la permission d'en faire ce que l'on veut. Notez qu'en contrepartie, si
on essaye d'accéder à des sous-propriétés qui n'existent pas, on va générer des erreurs.
🧩 Extraire une donnée un peu plus enfouie
Disons qu'on souhaite extraire l'URL de l'image de taille medium
pour la glisser dans un élément <img>
plus tard :

C'est un peu plus pimenté 🌶 puisqu'il y a un tableau impliqué (nommé image
) et le damné caractère #
dans le nom de la propriété #text
.
Voici comment extraire la propriété #nom
:
async getSongs(){
let x = await lastValueFrom(this.http.get<any>("http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=9a8a3facebbccaf363bb9fd68fa37abf&artist=Cher&album=Believe&format=json"));
console.log(x);
let urlImageMedium : string = x.album.image[1]["#text"];
}
Pourquoi ce n'est pas plutôt
x.album.image[1].#text
?
S'il n'y avait pas eu le caractère #
dans le nom de la propriété text
, cela aurait été possible d'utiliser x.album.image[1].text
!
Hélas, #
est un symbole spécial délicat et pour pouvoir l'utiliser dans le nom d'une propriété, il faut absolument remplacer la syntaxe
.nomPropriété
par ["nomPropriété"]
pour ne pas que le symbole #
cause un problème.
🎨 Intégration des données dans la page Web
Faisons le nécessaire pour pouvoir afficher les données que nous avons extraites de l'objet JSON dans la page Web.
1 - 📦 Préparer des variables pour stocker les données à afficher
export class AppComponent implements OnInit {
artistName : string = "";
albumName : string = "";
imageUrl : string = "";
...
}
2 - 🚚 Ranger les données extraites dans ces variables
async getSongs(){
let x = await lastValueFrom(this.http.get<any>("http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=9a8a3facebbccaf363bb9fd68fa37abf&artist=Cher&album=Believe&format=json"));
console.log(x);
this.artistName = x.album.artist;
this.albumName = x.album.name;
this.imageUrl = x.album.image[1]["#text"];
}
3 - 🖼 Afficher les variables dans le HTML
(Vous remarquerez également un bouton qui permet de lancer la requête)
<button (click)="getSongs()">Chansons de Believe par Cher</button>
<p>Artiste : {{artistName}}</p>
<p>Album : {{albumName}}</p>
<img src="{{imageUrl}}" alt="Pochette de l'album {{albumName}}">

📜 Extraire un tableau de données
Disons que je souhaite extraire la liste des chansons de l'objet JSON (Le titre et la durée en secondes pour chaque chanson) ...

⚱ 1 - Préparer un model (au besoin)
export class Song{
constructor(
public name : string,
public duration : number
){}
}
📦 2 - Préparer un tableau pour y ranger les données
export class AppComponent implements OnInit {
artistName : string = "";
albumName : string = "";
imageUrl : string = "";
songs : Song[] = []; // Initialisé avec un tableau vide !
...
}