Architecture Client-Serveur : Flutter et Spring Boot
Sommaire
- 1- Objectif
- 2- Partie 1 : Architecture Client-Serveur - Théorie
- 2.1- Définition du Modèle Client-Serveur
- 2.1.1- Client (Flutter)
- 2.1.2- Serveur (Spring Boot)
- 2.2- Schéma de Communication : Flutter ↔ Spring Boot ↔ Base de Données
- 2.3- Protocole de Communication
- 3- API REST : Explication Complète
- 3.1- Définition Simple
- 3.2- Définition Technique
- 3.3- Les 6 Principes Fondamentaux du REST
- 3.3.1- Architecture Client-Serveur
- 3.3.2- Sans État (Stateless)
- 3.3.3- Mise en Cache (Cacheable)
- 3.3.4- Interface Uniforme
- 3.3.5- Système en Couches
- 3.3.6- Code à la Demande (Optionnel)
- 3.4- Structure d'une API REST
- 3.5- Formats de Données
- 3.6- Exemple Complet : API de Produits
- 3.7- Avantages des APIs REST
- 3.8- REST vs Alternatives
- 3.9- Sécurité d'une API REST
- 3.10- Outils pour APIs REST
- 4- Qu'est-ce qu'un endpoint API ?
- 4.1- Définitions
- 4.2- Structure d'un endpoint API
- 4.3- Méthodes HTTP et leurs utilisations
- 4.4- Bonnes pratiques pour concevoir des endpoints API
- 4.5- Exemple complet avec Spring Boot et Flutter
- 4.5.1- Côté Spring Boot (Backend)
- 4.5.2- Côté Flutter (Frontend)
- 4.5.3- Cours Flutter
Architecture Client-Serveur : Flutter et Spring Boot
-
Objectif
- À la fin de ce tutoriel, vous serez capable de :
- Comprendre le principe de l’architecture client-serveur
- Configurer un serveur Spring Boot avec une API REST
- Développer une application Flutter consommant l’API
- Gérer la communication et les échanges de données
- Gérer les erreurs et la sécurité basique
-
Partie 1 : Architecture Client-Serveur – Théorie
-
Définition du Modèle Client-Serveur
- Le modèle Client–Serveur est une architecture logicielle qui sépare clairement les responsabilités entre deux parties principales :
-
Client (Flutter)
- Le client correspond à l’application que l’utilisateur utilise directement.
- Application mobile / desktop développée avec Flutter.
- Interface utilisateur (UI) permettant l’interaction avec l’utilisateur. Envoie des requêtes HTTP (GET, POST, PUT, DELETE) vers le serveur.
- Reçoit les réponses (souvent au format JSON) et les affiche sous forme lisible (liste, tableau, formulaire…).
-
Serveur (Spring Boot)
- Le serveur est la partie backend qui centralise la logique métier.
- Traite les requêtes envoyées par le client.
- Gère la logique métier (validation, calculs, règles d’entreprise).
- Accède à la base de données pour lire, écrire, modifier ou supprimer des informations.
- Renvoie des réponses au client (succès, erreur, données demandées).
- Flutter (Client) = interface et interaction avec l’utilisateur.
- Spring Boot (Serveur) = traitement des données et communication avec la base.
-
Schéma de Communication : Flutter ↔ Spring Boot ↔ Base de Données
- 1. Envoi de la requête (Client → Serveur)
- L’utilisateur interagit avec l’application Flutter (exemple : appuie sur « Voir les utilisateurs »).
- Flutter envoie une requête HTTP GET vers le serveur Spring Boot (exemple : http://localhost:8080/api/users).
- Cette requête demande au serveur des informations précises.
- 2. Traitement côté serveur (Spring Boot)
- Spring Boot reçoit la requête.
- Il applique la logique métier (ex. vérifier les droits d’accès, appliquer des règles, etc.).
- Ensuite, Spring Boot interroge la base de données MySQL via JPA/Hibernate.
- La base renvoie les données demandées (ex. liste d’utilisateurs).
- 3. Réponse du serveur (Serveur → Client)
- Spring Boot convertit les données récupérées en format JSON.
- Il renvoie la réponse au client Flutter.
- Flutter reçoit ce JSON, le décode et l’affiche à l’utilisateur (ex. une liste d’utilisateurs dans un ListView).
-
Protocole de Communication
- HTTP/HTTPS : Protocole de transport
- REST : Architecture d’API
- JSON : Format d’échange de données
- Stateless : Chaque requête est indépendante
-
API REST : Explication Complète
-
Définition Simple
- Une API REST est comme un serveur dans un restaurant :
- Le client (Flutter) consulte le menu (endpoints API)
- Il passe commande (requête HTTP)
- Le serveur (Spring Boot) comprend la commande, va en cuisine (base de données)
- Il rapporte le plat (réponse JSON/XML)
- Chaque interaction est indépendante : le serveur ne se souvient pas des commandes précédentes
-
Définition Technique
- REST (REpresentational State Transfer) est une architecture logicielle qui définit des règles pour créer des services web. Une API REST respecte ces règles pour permettre à des applications de communiquer entre elles via HTTP.
-
Les 6 Principes Fondamentaux du REST
-
Architecture Client-Serveur
- Séparation nette entre le client et le serveur
- Le client gère l’interface utilisateur
- Le serveur gère le stockage des données et la logique métier
- Évolution indépendante possible
-
Sans État (Stateless)
- Chaque requête doit contenir toutes les informations nécessaires
- Le serveur ne garde pas le contexte du client entre les requêtes
- Exemple : Le token d’authentification doit être envoyé à chaque requête
-
Mise en Cache (Cacheable)
- Les réponses doivent indiquer si elles peuvent être mises en cache
- Améliore les performances et réduit la charge serveur
- Exemple :
Cache-Control: max-age=3600
-
Interface Uniforme
- Standardisation de la communication :
- Identification des ressources (URLs claires)
- Manipulation via représentations (JSON, XML)
- Messages auto-descriptifs
- Hypermedia comme moteur de l’état de l’application (HATEOAS)
-
Système en Couches
- Architecture en couches hiérarchisées
- Le client ignore s’il communique avec le serveur final ou un intermédiaire
- Permet une meilleure scalabilité et sécurité
-
Code à la Demande (Optionnel)
- Possibilité d’envoyer du code exécutable au client
- Moins utilisé dans les APIs modernes
-
Structure d’une API REST
- URLs (Endpoints)
- Méthodes HTTP
- Codes de Statut HTTP
-
Formats de Données
- JSON (le plus courant)
- XML (moins utilisé aujourd’hui)
-
Exemple Complet : API de Produits
- GET /api/products (Lister tous les produits)
- POST /api/products (Créer un produit)
-
Avantages des APIs REST
- Simplicité : Utilise le protocole HTTP standard
- Interopérabilité : Compatible avec tous les langages et plateformes
- Scalabilité : Architecture sans état, facile à scaler
- Flexibilité : Supporte plusieurs formats (JSON, XML)
- Performance : Mise en cache native HTTP
-
REST vs Alternatives
- REST vs SOAP
- REST vs GraphQL
-
Sécurité d’une API REST
- Authentification
- Tokens JWT
- OAuth2 pour autorisation déléguée
- API Keys
- Protection
- HTTPS obligatoire
- Validation des entrées
- Rate Limiting
- Configuration CORS
-
Outils pour APIs REST
- Postman → Test et documentation
- Swagger/OpenAPI → Documentation automatique
- curl → Ligne de commande
-
Qu’est-ce qu’un endpoint API ?
-
Définitions
- Un endpoint API est un point d’accès spécifique à une API (Application Programming Interface) qui permet à différentes applications de communiquer entre elles. Chaque endpoint correspond à une URL spécifique qui expose une ressource ou une fonctionnalité particulière.
- Analogie avec un restaurant
- Imaginez une API comme le menu d’un restaurant :
- L’API est le menu entier
- Chaque endpoint est un plat spécifique du menu
- La requête est la commande passée par le client
- La réponse est le plat servi par le restaurant
- Dans le contexte du développement web, les endpoints API permettent à votre application frontend (comme une app Flutter) de communiquer avec le backend (comme Spring Boot) pour récupérer, créer, modifier ou supprimer des données.
-
Structure d’un endpoint API
- Un endpoint API est généralement constitué de plusieurs éléments :
- Exemple d’endpoint API complet
- GET https://api.example.com/users/123/posts?published=true&limit=5
- Protocole : https://
- Domaine : api.example.com
- Chemin : /users/123/posts
- Paramètres de requête : ?published=true&limit=5
- Composants principaux
- Méthode HTTP : Définit le type d’opération (GET, POST, PUT, DELETE, etc.)
- URL de base : Point d’entrée principal de l’API (ex: https://api.example.com)
- Chemin de la ressource : Spécifie la ressource cible (ex: /users/123/posts)
- Paramètres : Filtres, options ou données supplémentaires
-
Méthodes HTTP et leurs utilisations
- Les endpoints API utilisent différentes méthodes HTTP :
- GET : Récupérer des données (lecture seule)
- POST : Créer une nouvelle ressource
- PUT : Mettre à jour une ressource existante (remplacement complet)
- PATCH : Mettre à jour partiellement une ressource
- DELETE : Supprimer une ressource
- Exemples concrets
- GET /api/users → Récupère la liste des utilisateurs
- GET /api/users/42 → Récupère l’utilisateur avec l’ID 42
- POST /api/users → Crée un nouvel utilisateur
- PUT /api/users/42 → Met à jour complètement l’utilisateur 42
- DELETE /api/users/42 → Supprime l’utilisateur 42
-
Bonnes pratiques pour concevoir des endpoints API
- Utiliser des noms pluriels : /api/users plutôt que /api/user
- Hiérarchie logique : /api/users/123/posts pour les posts de l’utilisateur 123
- Versioning de l’API : /api/v1/users
- Utiliser des verbes pour les actions complexes : /api/users/123/activate
- Filtres et pagination : /api/users?active=true&page=2&limit=20
- Utiliser les bons codes HTTP (200, 201, 400, 404, 500, etc.)
-
Exemple complet avec Spring Boot et Flutter
-
Côté Spring Boot (Backend)
-
Côté Flutter (Frontend)
GET /api/users → Liste tous les utilisateurs GET /api/users/1 → Récupère l'utilisateur avec ID=1 POST /api/users → Crée un nouvel utilisateur PUT /api/users/1 → Met à jour l'utilisateur ID=1 DELETE /api/users/1 → Supprime l'utilisateur ID=1
Méthode | Action | Exemple |
---|---|---|
GET | Récupérer | GET /api/products |
POST | Créer | POST /api/products |
PUT | Mettre à jour (complet) | PUT /api/products/1 |
PATCH | Mettre à jour (partiel) | PATCH /api/products/1 |
DELETE | Supprimer | DELETE /api/products/1 |
Code | Signification | Exemple |
---|---|---|
200 | OK – Succès | Requête réussie |
201 | Created – Créé | Ressource créée |
400 | Bad Request | Données invalides |
401 | Unauthorized | Token manquant/invalide |
403 | Forbidden | Permissions insuffisantes |
404 | Not Found | Ressource inexistante |
500 | Internal Error | Bug côté serveur |
{ "id": 1, "name": "Smartphone", "price": 299.99, "inStock": true }
1 Smartphone 299.99 true
GET /api/products HTTP/1.1 Host: api.moncommerce.com Authorization: Bearer <token> Réponse : HTTP/1.1 200 OK Content-Type: application/json [ { "id": 1, "name": "Smartphone", "price": 299.99 }, { "id": 2, "name": "Laptop", "price": 899.99 } ]
POST /api/products HTTP/1.1 Host: api.moncommerce.com Content-Type: application/json Authorization: Bearer <token> { "name": "Tablet", "price": 199.99 } Réponse : HTTP/1.1 201 Created Content-Type: application/json Location: /api/products/3 { "id": 3, "name": "Tablet", "price": 199.99 }
Aspect | REST | SOAP |
---|---|---|
Format | JSON, XML | XML seulement |
Performance | Léger | Lourd |
Courbe d’apprentissage | Facile | Complexe |
Flexibilité | Élevée | Faible |
Aspect | REST | GraphQL |
---|---|---|
Requêtes | Multiple endpoints | Single endpoint |
Données | Fixe | Flexible |
Complexité | Simple | Modérée |
Caching | HTTP natif | Personnalisé |
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping
public List getAllUsers() {
return userService.getAllUsers();
}
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.getUserById(id);
}
@PostMapping
public User createUser(@RequestBody User user) {
return userService.createUser(user);
}
}
class ApiService {
static const String baseUrl = "http://10.0.2.2:8080/api";
static Future> getUsers() async {
final response = await http.get(Uri.parse('$baseUrl/users'));
if (response.statusCode == 200) {
List data = json.decode(response.body);
return data.map((json) => User.fromJson(json)).toList();
} else {
throw Exception('Erreur lors du chargement des utilisateurs');
}
}
static Future createUser(User user) async {
final response = await http.post(
Uri.parse('$baseUrl/users'),
headers: {'Content-Type': 'application/json'},
body: json.encode(user.toJson()),
);
if (response.statusCode == 201) {
return User.fromJson(json.decode(response.body));
} else {
throw Exception('Erreur lors de la création');
}
}
}