Module 4Réaliser un site web avec l'architecture MVC
Chapitre 10 sur 11
Découvrir les web services et les API REST
Comprendre SOAP et REST, créer une API REST en PHP et la consommer en PHP et en JavaScript.
- ≈ 135 min
- 5 leçons
- 1 vidéo
- 3 exercices
Module 4Réaliser un site web avec l'architecture MVC
Chapitre 10 sur 11
Comprendre SOAP et REST, créer une API REST en PHP et la consommer en PHP et en JavaScript.
Objectifs
Avec ce chapitre, tu sauras…
SoapClient ;Leçon 1
| Type d'API | Accès |
|---|---|
| ouverte (publique) | disponible sans restriction (souvent avec une clé gratuite) |
| partenaire | réservée à des partenaires, avec une licence ou des droits d'accès |
| interne (privée) | utilisée par l'entreprise pour ses propres applications (site web, application mobile) |
| Avantages | Inconvénient |
|---|---|
| accéder à des fonctionnalités via Internet sans tout stocker chez soi | il faut toujours un réseau pour fonctionner |
| un serveur répond à beaucoup de clients en même temps | |
| des standards ouverts : grande compatibilité | |
| échanges chiffrables avec HTTPS | |
| un même back-end pour le site web, l'application mobile et les partenaires |
Leçon 2
| Élément du WSDL | Rôle |
|---|---|
<definitions> | la racine du document |
<types>, <message> | les types de données et les messages échangés |
<portType> / <interface> | les opérations disponibles |
<binding> | le protocole utilisé pour appeler le service |
<port> / <endpoint> |
Leçon 3
| Contrainte | Signification |
|---|---|
| client-serveur | les responsabilités sont séparées : l'interface d'un côté, les données de l'autre |
| sans état | chaque requête contient tout ce qu'il faut pour être comprise ; le serveur ne garde pas de session entre deux requêtes (l'authentification passe par un jeton envoyé à chaque fois) |
| mise en cache | les réponses indiquent si elles peuvent être mises en cache |
| système en couches | le client ne sait pas s'il parle au serveur final ou à un intermédiaire (proxy, répartiteur de charge) |
Leçon 4
| Élément | Rôle |
|---|---|
header('Content-Type: application/json') | annonce une réponse JSON |
http_response_code(201) | le code d'état adapté à l'opération |
file_get_contents('php://input') | lit le corps brut de la requête : avec du JSON, $_POST reste vide |
json_decode(…, true) / json_encode(…) | JSON vers tableau / tableau vers JSON |
JSON_UNESCAPED_UNICODE | garde les accents lisibles (é au lieu de é) |
Leçon 5
Vidéo · À regarder
ENJavaScript Full Course for free
Bro Code · YouTube
1SOAP échange des messages au format…
2Le fichier qui décrit un service SOAP est…
3Dans une API REST, l'URI d'un stagiaire est plutôt…
4Pour créer une ressource, on utilise…
5Le code renvoyé après une création réussie est…
6« Sans état » signifie que…
7Pour lire un corps JSON envoyé à PHP :
8L'en-tête qui annonce une réponse JSON est…
L'essentiel du chapitre
SoapClient, SoapServer, SoapFault.header('Content-Type: application/json'), http_response_code(), php://input + json_decode, json_encode, routeur + .htaccess.file_get_contents / cURL en PHP, fetch en JavaScript ; tester avec Postman, Thunder Client ou curl.| l'adresse réelle du service |
<service> | l'ensemble des points d'accès |
| interface uniforme | des conventions communes : URI, méthodes, codes, formats |
| code à la demande (facultatif) | le serveur peut envoyer du code exécutable au client (du JavaScript) |
| Bon | Mauvais |
|---|---|
/api/stagiaires | /api/getStagiaires |
/api/stagiaires/12 | /api/stagiaire.php?action=voir&id=12 |
/api/stagiaires?filiere=dev&tri=nom | /api/stagiairesDeDev |
/api/stagiaires/12/notes | /api/notesDuStagiaire12 |
| Méthode | URI | Action | Code de succès |
|---|---|---|---|
| GET | /api/stagiaires | lire la liste | 200 OK |
| GET | /api/stagiaires/12 | lire un stagiaire | 200 OK (404 s'il n'existe pas) |
| POST | /api/stagiaires | créer un stagiaire | 201 Created |
| PUT | /api/stagiaires/12 | remplacer le stagiaire 12 | 200 OK |
| PATCH | /api/stagiaires/12 | modifier une partie du stagiaire 12 | 200 OK |
| DELETE | /api/stagiaires/12 | supprimer le stagiaire 12 | 204 No Content |
| Code | Quand le renvoyer |
|---|---|
| 200 OK | lecture ou modification réussie |
| 201 Created | ressource créée (avec l'en-tête Location vers la nouvelle ressource) |
| 204 No Content | suppression réussie, pas de corps |
| 400 Bad Request | données invalides |
| 401 Unauthorized | non authentifié (jeton absent ou invalide) |
| 403 Forbidden | authentifié, mais pas le droit |
| 404 Not Found | ressource inexistante |
| 405 Method Not Allowed | méthode non gérée sur cette URI |
| 409 Conflict | conflit (e-mail déjà utilisé) |
| 500 Internal Server Error | erreur du serveur |
| SOAP | REST | |
|---|---|---|
| nature | protocole strict | style d'architecture |
| format | XML uniquement | JSON (surtout), XML, HTML… |
| description | WSDL obligatoire | facultative (OpenAPI, ex-Swagger) |
| transport | HTTP, SMTP… | HTTP |
| poids | messages lourds | messages légers |
| usage | systèmes d'entreprise, transactions | applications web et mobiles, API publiques |
Avec l'API de la section 4 et la table des 3 stagiaires du chapitre 7, donne le code d'état et le contenu (en résumé) de chaque requête : (1) GET /api/stagiaires/2 ; (2) GET /api/stagiaires/99 ; (3) POST /api/stagiaires avec {"nom": "Tazi"} ; (4) DELETE /api/stagiaires/3 ; (5) PATCH /api/stagiaires/1 ; (6) GET /api/formations.
SOAP (Simple Object Access Protocol) est un protocole standardisé par le W3C, basé sur XML, pour échanger des messages entre applications, en général sur HTTP. Un service SOAP est décrit par un fichier WSDL (Web Services Description Language), un document XML qui liste les opérations disponibles, leurs paramètres et leurs types. Historiquement, l'annuaire UDDI permettait de découvrir les services.
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Header/>
<soap:Body>
<getMoyenne xmlns="http://ofppt.example/stagiaires">
<idStagiaire>12</idStagiaire>
</getMoyenne>
</soap:Body>
</soap:Envelope>REST (Representational State Transfer) est un style d'architecture décrit par Roy Fielding en 2000. Une API REST (ou RESTful) expose des ressources (stagiaires, commentaires, commandes), identifiées par des URI, et manipulées avec les méthodes HTTP standard. Les réponses sont des représentations des ressources, le plus souvent en JSON. REST n'invente pas de surcouche : il utilise HTTP tel qu'il a été conçu. Le web lui-même est une application REST.
Tableau: Les six contraintes de REST
On reprend la démarche du résumé (l'API d'items : create, read, update, delete), appliquée aux stagiaires, avec un seul point d'entrée qui suit les règles REST.
api/
├── .htaccess ← toutes les URL /api/... vers index.php
├── index.php ← routeur : méthode HTTP + URI → action
└── StagiaireApi.php ← les actions (utilise PDO)RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]<?php
declare(strict_types=1);
require __DIR__ . '/../config/database.php';
header('Content-Type: application/json; charset=utf-8');
function repondre(int $code, mixed $donnees = null): never
{
http_response_code($code);
if ($donnees !== null) {
echo json_encode($donnees, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}
exit;
}
// /api/stagiaires/12 → ['stagiaires', '12']
$chemin = trim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/');
$segments = array_values(array_slice(explode('/', $chemin), 1)); // on retire "api"
$ressource = $segments[0] ?? '';
$id = isset($segments[1]) ? (int) $segments[1] : null;
$methode = $_SERVER['REQUEST_METHOD'];
if ($ressource !== 'stagiaires') {
repondre(404, ['erreur' => 'Ressource inconnue']);
}
$pdo = connexion();
try {
switch (true) {
case $methode === 'GET' && $id === null:
$stmt = $pdo->query('SELECT id, nom, prenom, email, filiere FROM stagiaires ORDER BY nom');
repondre(200, $stmt->fetchAll(PDO::FETCH_ASSOC));
case $methode === 'GET':
$stmt = $pdo->prepare('SELECT id, nom, prenom, email, filiere FROM stagiaires WHERE id = ?');
$stmt->execute([$id]);
$s = $stmt->fetch(PDO::FETCH_ASSOC);
$s ? repondre(200, $s) : repondre(404, ['erreur' => 'Stagiaire introuvable']);
case $methode === 'POST' && $id === null:
$d = json_decode(file_get_contents('php://input'), true) ?? []; // corps JSON de la requête
foreach (['nom', 'prenom', 'email', 'filiere'] as $champ) {
if (empty($d[$champ])) {
repondre(400, ['erreur' => "Champ manquant : $champ"]);
}
}
if (!filter_var($d['email'], FILTER_VALIDATE_EMAIL)) {
repondre(400, ['erreur' => 'E-mail invalide']);
}
$stmt = $pdo->prepare('INSERT INTO stagiaires (nom, prenom, email, filiere) VALUES (?, ?, ?, ?)');
$stmt->execute([$d['nom'], $d['prenom'], $d['email'], $d['filiere']]);
$nouvelId = (int) $pdo->lastInsertId();
header("Location: /api/stagiaires/$nouvelId");
repondre(201, ['id' => $nouvelId] + $d);
case $methode === 'PUT' && $id !== null:
$d = json_decode(file_get_contents('php://input'), true) ?? [];
$stmt = $pdo->prepare('UPDATE stagiaires SET nom = ?, prenom = ?, email = ?, filiere = ? WHERE id = ?');
$stmt->execute([$d['nom'] ?? '', $d['prenom'] ?? '', $d['email'] ?? '', $d['filiere'] ?? '', $id]);
repondre(200, ['id' => $id] + $d);
case $methode === 'DELETE' && $id !== null:
$stmt = $pdo->prepare('DELETE FROM stagiaires WHERE id = ?');
$stmt->execute([$id]);
$stmt->rowCount() ? repondre(204) : repondre(404, ['erreur' => 'Stagiaire introuvable']);
default:
repondre(405, ['erreur' => 'Méthode non autorisée']);
}
} catch (PDOException $e) {
error_log($e->getMessage());
$doublon = ($e->errorInfo[1] ?? null) === 1062;
repondre($doublon ? 409 : 500, ['erreur' => $doublon ? 'E-mail déjà utilisé' : 'Erreur serveur']);
}Depuis PHP
<?php
$json = file_get_contents('https://jsonplaceholder.typicode.com/users/1');
$utilisateur = json_decode($json, true);
echo $utilisateur['name'] . ' habite à ' . $utilisateur['address']['city'];
// avec cURL : contrôle complet (méthode, en-têtes, corps, délai)
$ch = curl_init('https://jsonplaceholder.typicode.com/posts');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode(['title' => 'Bonjour', 'body' => 'Test', 'userId' => 1]),
CURLOPT_TIMEOUT => 10,
]);
$reponse = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE); // 201
curl_close($ch);Depuis JavaScript (M105)
async function chargerStagiaires() {
const reponse = await fetch('/api/stagiaires');
if (!reponse.ok) throw new Error(`Erreur HTTP ${reponse.status}`);
const stagiaires = await reponse.json();
const liste = document.querySelector('#liste');
stagiaires.forEach(s => {
const li = document.createElement('li');
li.textContent = `${s.prenom} ${s.nom} (${s.filiere})`;
liste.append(li);
});
}
async function supprimerStagiaire(id) {
const reponse = await fetch(`/api/stagiaires/${id}`, { method: 'DELETE' });
return reponse.status === 204;
}C'est l'architecture des applications modernes : le back-end PHP expose une API JSON, et le front-end (JavaScript, React, une application mobile) l'utilise.
Consommer une API avec fetch en JavaScript (en anglais).
Ajoute à ton projet l'API REST de la section 4. Complète-la : filtre ?filiere=dev pour la liste, validation complète pour PUT (400 si un champ manque), méthode PATCH (modification partielle), et une page annuaire.html en JavaScript qui affiche la liste avec fetch, permet d'ajouter un stagiaire par un formulaire (POST en JSON) et de le supprimer (DELETE), sans jamais recharger la page.
Une application de bibliothèque doit exposer une API REST. Donne la méthode, l'URI et le code de succès pour : (1) lister les livres d'un genre donné ; (2) afficher le livre d'ISBN 9789954…; (3) ajouter un livre ; (4) corriger seulement le titre d'un livre ; (5) supprimer un livre ; (6) lister les prêts en cours d'un adhérent ; (7) enregistrer le retour d'un prêt.
SOAP en PHP
L'extension soap (à activer dans php.ini : extension=soap) fournit les classes SoapClient, SoapServer, SoapFault, SoapHeader, SoapParam et SoapVar.
<?php
try {
$client = new SoapClient('https://exemple.ma/service?wsdl', [
'exceptions' => true,
'cache_wsdl' => WSDL_CACHE_DISK,
]);
print_r($client->__getFunctions()); // les opérations proposées par le WSDL
$resultat = $client->getMoyenne(['idStagiaire' => 12]); // appel comme une méthode PHP
echo $resultat->moyenne;
} catch (SoapFault $e) {
echo 'Erreur SOAP : ' . $e->faultcode . ' - ' . $e->getMessage();
}<?php
class ServiceStagiaires
{
public function getMoyenne(int $idStagiaire): float
{
return 15.25; // en réalité : une requête PDO
}
}
$serveur = new SoapServer(null, ['uri' => 'http://ofppt.example/stagiaires']);
$serveur->setClass(ServiceStagiaires::class);
$serveur->handle(); // lit la requête SOAP, appelle la méthode, renvoie la réponse XMLLes quatre règles d'une API REST sur HTTP
Règle 1 : l'URI identifie la ressource. On utilise des noms au pluriel, jamais de verbes.
Règle 2 : la méthode HTTP identifie l'opération (le CRUD du chapitre 7).
Règle 3 : la réponse est une représentation de la ressource. Le client indique le format souhaité dans l'en-tête Accept (application/json, application/xml, text/html) ; le serveur l'indique dans Content-Type.
Règle 4 : les liens expriment les relations entre ressources (principe HATEOAS) : la réponse peut contenir les URI des ressources liées.
{
"id": 12,
"nom": "Benali",
"prenom": "Sara",
"filiere": "dev",
"liens": {
"self": "/api/stagiaires/12",
"notes": "/api/stagiaires/12/notes"
}
}