Generated Endpoints API Reference¶
Référence complète des endpoints REST générés automatiquement par SpringFlow.
Vue d'Ensemble¶
Pour chaque entité annotée avec @AutoApi, SpringFlow génère automatiquement 6 endpoints REST suivant les conventions RESTful.
Format de Base:
Exemple:
- Configuration: springflow.base-path: /api
- Annotation: @AutoApi(path = "/products")
- Résultat: /api/products
Endpoints Générés¶
1. GET - Liste Paginée¶
Récupère une liste paginée d'entités avec support de tri et filtrage.
Signature:
Paramètres de Requête:
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
page |
int | 0 |
Numéro de page (0-based par défaut) |
size |
int | 20 |
Taille de la page |
sort |
string | - | Tri: field,asc ou field,desc |
Headers de Réponse:
| Header | Description |
|---|---|
Content-Type |
application/json |
X-Total-Count |
Nombre total d'éléments |
Corps de Réponse:
{
"content": [
{
"id": 1,
"name": "Product A",
"price": 29.99,
"createdAt": "2024-01-15T10:30:00"
},
{
"id": 2,
"name": "Product B",
"price": 49.99,
"createdAt": "2024-01-16T14:20:00"
}
],
"pageable": {
"pageNumber": 0,
"pageSize": 20,
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"offset": 0,
"paged": true,
"unpaged": false
},
"totalPages": 5,
"totalElements": 100,
"last": false,
"first": true,
"size": 20,
"number": 0,
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"numberOfElements": 20,
"empty": false
}
Codes de Status:
| Code | Description |
|---|---|
200 OK |
Succès |
400 Bad Request |
Paramètres invalides (ex: size > max-page-size) |
401 Unauthorized |
Authentification requise |
403 Forbidden |
Permissions insuffisantes |
Exemples:
# Première page avec taille par défaut
curl -X GET "http://localhost:8080/api/products"
# Page 2, 50 éléments par page
curl -X GET "http://localhost:8080/api/products?page=1&size=50"
# Trié par prix décroissant
curl -X GET "http://localhost:8080/api/products?sort=price,desc"
# Tri multiple: par nom ASC, puis prix DESC
curl -X GET "http://localhost:8080/api/products?sort=name,asc&sort=price,desc"
# Avec filtrage (si @Filterable configuré)
curl -X GET "http://localhost:8080/api/products?name_like=laptop&price_gte=100&price_lte=500"
2. GET - Par ID¶
Récupère une entité spécifique par son identifiant.
Signature:
Paramètres de Chemin:
| Paramètre | Type | Description |
|---|---|---|
id |
Long/String/Composite | Identifiant unique de l'entité |
Corps de Réponse:
{
"id": 1,
"name": "Product A",
"price": 29.99,
"stock": 100,
"category": "Electronics",
"createdAt": "2024-01-15T10:30:00"
}
Codes de Status:
| Code | Description |
|---|---|
200 OK |
Entité trouvée |
404 Not Found |
Entité inexistante |
401 Unauthorized |
Authentification requise |
403 Forbidden |
Permissions insuffisantes |
Exemples:
# ID numérique
curl -X GET "http://localhost:8080/api/products/42"
# ID String (UUID)
curl -X GET "http://localhost:8080/api/users/550e8400-e29b-41d4-a716-446655440000"
# Avec authentification Bearer
curl -X GET "http://localhost:8080/api/orders/123" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Réponse 404:
{
"timestamp": "2024-01-20T15:30:45.123Z",
"status": 404,
"error": "Not Found",
"message": "Product with id 999 not found",
"path": "/api/products/999"
}
3. POST - Créer¶
Crée une nouvelle entité.
Signature:
Headers de Requête:
| Header | Valeur |
|---|---|
Content-Type |
application/json |
Corps de Requête:
Notes:
- Les champs @Hidden sont ignorés (même s'ils sont fournis)
- Les champs @ReadOnly sont ignorés (ID, timestamps, etc.)
- Les champs @Id avec @GeneratedValue sont auto-générés
- Les validations JSR-380 avec groupe Create sont appliquées (v0.4.0+)
Corps de Réponse (201 Created):
{
"id": 101,
"name": "New Product",
"price": 39.99,
"stock": 50,
"category": "Books",
"createdAt": "2024-01-20T16:45:30"
}
Headers de Réponse:
| Header | Description |
|---|---|
Location |
URI de la ressource créée: /api/products/101 |
Content-Type |
application/json |
Codes de Status:
| Code | Description |
|---|---|
201 Created |
Entité créée avec succès |
400 Bad Request |
Données invalides (validation échouée) |
401 Unauthorized |
Authentification requise |
403 Forbidden |
Permissions insuffisantes |
409 Conflict |
Contrainte d'unicité violée |
Exemples:
# Création basique
curl -X POST "http://localhost:8080/api/products" \
-H "Content-Type: application/json" \
-d '{
"name": "Laptop Pro",
"price": 1299.99,
"stock": 10,
"category": "Electronics"
}'
# Avec authentification
curl -X POST "http://localhost:8080/api/orders" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-d '{
"customerId": 5,
"totalAmount": 299.99,
"items": [
{"productId": 1, "quantity": 2}
]
}'
Réponse 400 - Validation Échouée:
{
"timestamp": "2024-01-20T16:45:30.123Z",
"status": 400,
"error": "Bad Request",
"message": "Validation failed",
"errors": [
{
"field": "name",
"message": "Name is required",
"rejectedValue": null
},
{
"field": "price",
"message": "Price must be greater than 0",
"rejectedValue": -10.0
}
],
"path": "/api/products"
}
Réponse 409 - Contrainte d'Unicité:
{
"timestamp": "2024-01-20T16:45:30.123Z",
"status": 409,
"error": "Conflict",
"message": "Product with name 'Laptop Pro' already exists",
"path": "/api/products"
}
4. PUT - Mise à Jour Complète¶
Remplace complètement une entité existante.
Signature:
Headers de Requête:
| Header | Valeur |
|---|---|
Content-Type |
application/json |
Corps de Requête:
Comportement:
- Tous les champs doivent être fournis (sauf @ReadOnly et @Hidden)
- Les champs omis sont mis à null (sauf contraintes NOT NULL)
- Les champs @ReadOnly sont ignorés
- Les validations avec groupe Update sont appliquées (v0.4.0+)
Corps de Réponse (200 OK):
{
"id": 42,
"name": "Updated Product Name",
"price": 49.99,
"stock": 75,
"category": "Electronics",
"createdAt": "2024-01-15T10:30:00",
"updatedAt": "2024-01-20T17:00:00"
}
Codes de Status:
| Code | Description |
|---|---|
200 OK |
Mise à jour réussie |
400 Bad Request |
Données invalides |
404 Not Found |
Entité inexistante |
401 Unauthorized |
Authentification requise |
403 Forbidden |
Permissions insuffisantes |
409 Conflict |
Contrainte violée |
Exemples:
# Mise à jour complète
curl -X PUT "http://localhost:8080/api/products/42" \
-H "Content-Type: application/json" \
-d '{
"name": "Laptop Pro 2024",
"price": 1499.99,
"stock": 20,
"category": "Electronics"
}'
5. PATCH - Mise à Jour Partielle¶
Met à jour seulement les champs fournis.
Signature:
Headers de Requête:
| Header | Valeur |
|---|---|
Content-Type |
application/json |
Corps de Requête:
Comportement:
- Seuls les champs fournis sont mis à jour
- Les champs omis conservent leur valeur actuelle
- Les champs null sont traités comme "ne pas modifier" (pas comme "mettre à null")
- Les champs @ReadOnly sont rejetés avec erreur 400 (v0.4.0+)
- Les champs @Hidden sont rejetés avec erreur 400 (v0.4.0+)
- Les validations sont appliquées avec groupe Update (v0.4.0+)
Corps de Réponse (200 OK):
{
"id": 42,
"name": "Product A",
"price": 44.99,
"stock": 80,
"category": "Electronics",
"createdAt": "2024-01-15T10:30:00",
"updatedAt": "2024-01-20T17:15:00"
}
Codes de Status:
| Code | Description |
|---|---|
200 OK |
Mise à jour réussie |
400 Bad Request |
Données invalides |
404 Not Found |
Entité inexistante |
401 Unauthorized |
Authentification requise |
403 Forbidden |
Permissions insuffisantes |
409 Conflict |
Contrainte violée |
Exemples:
# Mettre à jour seulement le prix
curl -X PATCH "http://localhost:8080/api/products/42" \
-H "Content-Type: application/json" \
-d '{"price": 39.99}'
# Mettre à jour plusieurs champs
curl -X PATCH "http://localhost:8080/api/products/42" \
-H "Content-Type: application/json" \
-d '{
"price": 39.99,
"stock": 150,
"category": "Books"
}'
Différence PUT vs PATCH:
| Aspect | PUT | PATCH |
|---|---|---|
| Champs requis | Tous (sauf @ReadOnly) | Seulement ceux à modifier |
| Champs omis | Mis à null | Inchangés |
| Use Case | Remplacement complet | Modification partielle |
| Idempotence | Oui | Oui |
Validation de Champs (v0.4.0+)¶
Nouveau depuis v0.4.0
SpringFlow valide maintenant strictement les champs dans les requêtes PATCH, en rejetant les tentatives de modification de champs protégés.
Protection des Champs @Hidden:
@Entity
@AutoApi(path = "users")
public class User {
@Id
private Long id;
private String name;
@Hidden
private String passwordHash; // Protégé contre les modifications
}
Requête rejetée:
curl -X PATCH "http://localhost:8080/api/users/1" \
-H "Content-Type: application/json" \
-d '{"passwordHash": "hacked"}' # ❌ Rejeté
Réponse 400:
{
"timestamp": "2025-12-27T10:30:00",
"status": 400,
"error": "Bad Request",
"message": "Cannot update hidden field: passwordHash"
}
Protection des Champs @ReadOnly:
@Entity
@AutoApi(path = "products")
public class Product {
@Id
private Long id;
private String name;
@ReadOnly
private LocalDateTime createdAt; // Protégé contre les modifications
}
Requête rejetée:
curl -X PATCH "http://localhost:8080/api/products/1" \
-H "Content-Type: application/json" \
-d '{"createdAt": "2020-01-01T00:00:00"}' # ❌ Rejeté
Réponse 400:
{
"timestamp": "2025-12-27T10:30:00",
"status": 400,
"error": "Bad Request",
"message": "Cannot update read-only field: createdAt"
}
Validation Groups (v0.4.0+)¶
PATCH applique automatiquement le groupe Update pour les validations JSR-380.
Exemple avec Validation Conditionnelle:
@Entity
@AutoApi(path = "products")
public class Product {
@Id
private Long id;
@NotBlank(groups = {Create.class, Update.class})
private String name;
@NotNull(groups = Create.class, message = "Category required on creation")
private String initialCategory; // Requis à la création, optionnel en update
@Email(groups = Update.class, message = "Supplier email must be valid")
private String supplierEmail; // Validé UNIQUEMENT en update
}
PATCH avec validation Update:
curl -X PATCH "http://localhost:8080/api/products/1" \
-H "Content-Type: application/json" \
-d '{
"supplierEmail": "invalid-email" # ❌ Validation échoue (groupe Update)
}'
Réponse 400:
{
"timestamp": "2025-12-27T10:30:00",
"status": 400,
"errors": [
{
"field": "supplierEmail",
"message": "Supplier email must be valid",
"rejectedValue": "invalid-email",
"code": "Email",
"validationGroup": "Update"
}
]
}
PATCH réussi:
curl -X PATCH "http://localhost:8080/api/products/1" \
-H "Content-Type: application/json" \
-d '{
"supplierEmail": "supplier@example.com" # ✅ Valide
}'
Note: Le champ initialCategory n'est pas validé en PATCH car il n'appartient qu'au groupe Create.
6. DELETE - Suppression¶
Supprime une entité.
Signature:
Paramètres de Chemin:
| Paramètre | Type | Description |
|---|---|---|
id |
Long/String/Composite | Identifiant de l'entité à supprimer |
Corps de Réponse:
Aucun (204 No Content) ou confirmation JSON selon configuration.
Codes de Status:
| Code | Description |
|---|---|
204 No Content |
Suppression réussie |
404 Not Found |
Entité inexistante |
401 Unauthorized |
Authentification requise |
403 Forbidden |
Permissions insuffisantes |
409 Conflict |
Contraintes référentielles (FK) |
Exemples:
# Suppression simple
curl -X DELETE "http://localhost:8080/api/products/42"
# Avec authentification
curl -X DELETE "http://localhost:8080/api/orders/123" \
-H "Authorization: Bearer TOKEN"
Réponse 409 - Contrainte Référentielle:
{
"timestamp": "2024-01-20T17:30:00.123Z",
"status": 409,
"error": "Conflict",
"message": "Cannot delete product 42: referenced by 5 active orders",
"path": "/api/products/42"
}
Note: With @SoftDelete, deletion is logical (soft delete) rather than physical.
Filtrage Dynamique¶
Si des champs sont annotés avec @Filterable, des paramètres de requête supplémentaires sont disponibles sur l'endpoint GET liste.
Syntaxe des Filtres¶
| FilterType | Paramètre | Exemple |
|---|---|---|
| EQUALS | {field} |
?status=ACTIVE |
| LIKE | {field}_like |
?name_like=laptop |
| GREATER_THAN | {field}_gt |
?price_gt=100 |
| LESS_THAN | {field}_lt |
?price_lt=500 |
| GREATER_THAN_OR_EQUAL | {field}_gte |
?price_gte=100 |
| LESS_THAN_OR_EQUAL | {field}_lte |
?price_lte=500 |
| RANGE | {field}_gte + {field}_lte |
?price_gte=100&price_lte=500 |
| IN | {field}_in |
?status_in=ACTIVE,PENDING |
| NOT_IN | {field}_not_in |
?status_not_in=DELETED |
| IS_NULL | {field}_null |
?deletedAt_null=true |
| BETWEEN | {field}_between |
?createdAt_between=2024-01-01,2024-12-31 |
Exemples de Filtrage¶
Entité avec Filtres:
@Entity
@AutoApi(path = "/products")
public class Product {
@Id
private Long id;
@Filterable(types = {FilterType.LIKE, FilterType.EQUALS})
private String name;
@Filterable(types = FilterType.RANGE)
private BigDecimal price;
@Filterable(types = {FilterType.EQUALS, FilterType.IN})
@Enumerated(EnumType.STRING)
private ProductStatus status;
@Filterable(types = FilterType.RANGE)
private LocalDate releaseDate;
}
Requêtes Filtrées:
# Recherche par nom (contient "laptop")
curl -X GET "http://localhost:8080/api/products?name_like=laptop"
# Produits entre 100€ et 500€
curl -X GET "http://localhost:8080/api/products?price_gte=100&price_lte=500"
# Produits actifs ou en cours
curl -X GET "http://localhost:8080/api/products?status_in=ACTIVE,PENDING"
# Combinaison de filtres
curl -X GET "http://localhost:8080/api/products?name_like=laptop&price_gte=500&status=ACTIVE"
# Produits sans date de suppression (non supprimés)
curl -X GET "http://localhost:8080/api/products?deletedAt_null=true"
# Produits sortis en 2024
curl -X GET "http://localhost:8080/api/products?releaseDate_between=2024-01-01,2024-12-31"
Filtrage + Pagination + Tri:
curl -X GET "http://localhost:8080/api/products?name_like=laptop&price_gte=500&page=0&size=20&sort=price,asc"
Contrôle d'Exposition¶
L'annotation @AutoApi(expose = ...) contrôle quels endpoints sont générés.
Expose.ALL (Par Défaut)¶
Génère les 6 endpoints:
Endpoints:
- ✅ GET /api/products - Liste
- ✅ GET /api/products/{id} - Par ID
- ✅ POST /api/products - Créer
- ✅ PUT /api/products/{id} - Mise à jour complète
- ✅ PATCH /api/products/{id} - Mise à jour partielle
- ✅ DELETE /api/products/{id} - Supprimer
Expose.READ_ONLY¶
Lecture seule (GET uniquement):
Endpoints:
- ✅ GET /api/reports - Liste
- ✅ GET /api/reports/{id} - Par ID
- ❌ POST, PUT, PATCH, DELETE
Expose.CREATE_UPDATE¶
Pas de suppression physique:
Endpoints:
- ✅ GET /api/customers - Liste
- ✅ GET /api/customers/{id} - Par ID
- ✅ POST /api/customers - Créer
- ✅ PUT /api/customers/{id} - Mise à jour complète
- ✅ PATCH /api/customers/{id} - Mise à jour partielle
- ❌ DELETE
Use Case: Combined with @SoftDelete for logical deletion only.
Expose.CUSTOM¶
Aucun endpoint généré (implémentation custom complète):
Endpoints: Aucun (vous devez implémenter votre propre controller).
Sécurité des Endpoints¶
SecurityLevel.PUBLIC (Par Défaut)¶
Accessible sans authentification:
Tous les endpoints sont publics.
SecurityLevel.AUTHENTICATED¶
Authentification requise:
Effet:
- Header Authorization requis
- Utilisateur connecté nécessaire
- Tous les endpoints (GET, POST, PUT, PATCH, DELETE) protégés
SecurityLevel.ROLE_BASED¶
Rôles spécifiques requis:
@AutoApi(
path = "/admin/users",
security = @Security(
level = SecurityLevel.ROLE_BASED,
roles = {"ADMIN", "USER_MANAGER"}
)
)
Effet: - Utilisateur doit avoir un des rôles spécifiés - Vérifié via Spring Security - Tous les endpoints protégés
Sécurité Granulaire¶
Lecture publique, écriture protégée:
@AutoApi(
path = "/products",
security = @Security(
readLevel = SecurityLevel.PUBLIC,
writeLevel = SecurityLevel.ROLE_BASED,
roles = {"ADMIN", "INVENTORY_MANAGER"}
)
)
Effet:
- ✅ GET /api/products - Public
- ✅ GET /api/products/{id} - Public
- 🔒 POST /api/products - Requiert rôle ADMIN ou INVENTORY_MANAGER
- 🔒 PUT /api/products/{id} - Requiert rôle ADMIN ou INVENTORY_MANAGER
- 🔒 PATCH /api/products/{id} - Requiert rôle ADMIN ou INVENTORY_MANAGER
- 🔒 DELETE /api/products/{id} - Requiert rôle ADMIN ou INVENTORY_MANAGER
Réponse 401 Unauthorized:
{
"timestamp": "2024-01-20T18:00:00.123Z",
"status": 401,
"error": "Unauthorized",
"message": "Full authentication is required to access this resource",
"path": "/api/orders"
}
Réponse 403 Forbidden:
{
"timestamp": "2024-01-20T18:00:00.123Z",
"status": 403,
"error": "Forbidden",
"message": "Access Denied: requires role ADMIN",
"path": "/api/admin/users"
}
Format des DTOs¶
SpringFlow utilise des DTOs dynamiques basés sur Map<String, Object> au lieu de classes DTO fixes.
DTO de Sortie (Output DTO)¶
Contient tous les champs sauf:
- Champs annotés @Hidden
- Champs transient ou statiques
Exemple:
@Entity
public class User {
@Id
private Long id;
private String username;
private String email;
@Hidden
private String passwordHash;
@ReadOnly
private LocalDateTime createdAt;
}
DTO Retourné (GET):
❌ passwordHash n'est jamais dans la réponse.
DTO d'Entrée (Input DTO)¶
Contient tous les champs sauf:
- Champs annotés @Hidden
- Champs annotés @ReadOnly
- Champs annotés @Id (pour POST)
- Champs transient ou statiques
DTO Attendu (POST):
❌ id est auto-généré, ignoré si fourni.
❌ createdAt est en lecture seule, ignoré si fourni.
❌ passwordHash est caché, ignoré si fourni.
DTO Attendu (PUT/PATCH):
✅ id est dans l'URL, pas dans le corps.
❌ createdAt est en lecture seule, ignoré si fourni.
Format de Pagination¶
Réponse Page Spring Data¶
SpringFlow retourne le format standard Page<T> de Spring Data:
{
"content": [...], // Array des entités
"pageable": {
"pageNumber": 0, // Numéro de page actuelle
"pageSize": 20, // Taille de la page
"sort": {...}, // Info de tri
"offset": 0, // Offset global
"paged": true,
"unpaged": false
},
"totalPages": 10, // Nombre total de pages
"totalElements": 200, // Nombre total d'éléments
"last": false, // Dernière page?
"first": true, // Première page?
"size": 20, // Taille de la page
"number": 0, // Numéro de page
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"numberOfElements": 20, // Éléments dans cette page
"empty": false // Page vide?
}
Navigation entre Pages¶
Première Page:
Page Suivante:
Dernière Page (calculée depuis totalPages):
Détection de Fin:
- last: true → Pas de page suivante
- first: true → Pas de page précédente
- numberOfElements < size → Possiblement dernière page
Gestion des Erreurs¶
Format Standard des Erreurs¶
SpringFlow utilise le format d'erreur standard de Spring Boot:
{
"timestamp": "2024-01-20T18:30:00.123Z",
"status": 400,
"error": "Bad Request",
"message": "Detailed error message",
"path": "/api/products"
}
Erreurs de Validation (400)¶
{
"timestamp": "2024-01-20T18:30:00.123Z",
"status": 400,
"error": "Bad Request",
"message": "Validation failed",
"errors": [
{
"field": "email",
"message": "Email should be valid",
"rejectedValue": "invalid-email"
},
{
"field": "age",
"message": "Age must be at least 18",
"rejectedValue": 15
}
],
"path": "/api/users"
}
Codes de Status Communs¶
| Code | Nom | Quand |
|---|---|---|
| 200 | OK | GET, PUT, PATCH réussis |
| 201 | Created | POST réussi |
| 204 | No Content | DELETE réussi |
| 400 | Bad Request | Validation échouée, données invalides |
| 401 | Unauthorized | Authentification manquante |
| 403 | Forbidden | Permissions insuffisantes |
| 404 | Not Found | Entité inexistante |
| 409 | Conflict | Contrainte d'unicité, contrainte FK |
| 500 | Internal Server Error | Erreur serveur inattendue |
Exemples Complets par Scénario¶
Scénario 1: CRUD Basique (Produits)¶
Entité:
@Entity
@AutoApi(path = "/products")
public class Product {
@Id @GeneratedValue
private Long id;
@NotBlank
private String name;
@Min(0)
private BigDecimal price;
private Integer stock;
}
Opérations:
# 1. Créer un produit
curl -X POST "http://localhost:8080/api/products" \
-H "Content-Type: application/json" \
-d '{"name": "Laptop", "price": 999.99, "stock": 10}'
# Réponse: {"id": 1, "name": "Laptop", "price": 999.99, "stock": 10}
# 2. Lister les produits
curl -X GET "http://localhost:8080/api/products?page=0&size=10"
# 3. Récupérer le produit #1
curl -X GET "http://localhost:8080/api/products/1"
# 4. Mettre à jour le prix (PATCH)
curl -X PATCH "http://localhost:8080/api/products/1" \
-H "Content-Type: application/json" \
-d '{"price": 899.99}'
# 5. Supprimer le produit
curl -X DELETE "http://localhost:8080/api/products/1"
Scénario 2: Filtrage et Pagination (Blog)¶
Entité:
@Entity
@AutoApi(path = "/posts")
public class BlogPost {
@Id @GeneratedValue
private Long id;
@Filterable(types = FilterType.LIKE)
private String title;
@Filterable(types = {FilterType.EQUALS, FilterType.IN})
@Enumerated(EnumType.STRING)
private PostStatus status;
@Filterable(types = FilterType.RANGE)
private LocalDate publishedAt;
@ReadOnly
private Integer viewCount;
}
Requêtes:
# Articles publiés
curl -X GET "http://localhost:8080/api/posts?status=PUBLISHED"
# Recherche "spring" dans le titre
curl -X GET "http://localhost:8080/api/posts?title_like=spring"
# Articles publiés en janvier 2024
curl -X GET "http://localhost:8080/api/posts?publishedAt_gte=2024-01-01&publishedAt_lte=2024-01-31"
# Combinaison: publiés + recherche + tri par vues
curl -X GET "http://localhost:8080/api/posts?status=PUBLISHED&title_like=spring&sort=viewCount,desc"
# Page 2, 20 par page
curl -X GET "http://localhost:8080/api/posts?status=PUBLISHED&page=1&size=20"
Scénario 3: Sécurité (Commandes)¶
Entité:
@Entity
@AutoApi(
path = "/orders",
security = @Security(level = SecurityLevel.AUTHENTICATED)
)
public class Order {
@Id @GeneratedValue
private Long id;
@NotNull
private Long customerId;
@Min(0)
private BigDecimal totalAmount;
@ReadOnly
private LocalDateTime createdAt;
}
Requêtes:
# ❌ Échoue sans authentification
curl -X GET "http://localhost:8080/api/orders"
# Réponse: 401 Unauthorized
# ✅ Avec token JWT
curl -X GET "http://localhost:8080/api/orders" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
# ✅ Créer une commande (authentifié)
curl -X POST "http://localhost:8080/api/orders" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-d '{"customerId": 5, "totalAmount": 299.99}'
Scénario 4: Lecture Seule (Rapports)¶
Entité:
@Entity
@AutoApi(
path = "/reports",
expose = Expose.READ_ONLY
)
public class Report {
@Id @GeneratedValue
private Long id;
private String title;
@ReadOnly
private LocalDateTime generatedAt;
}
Endpoints Disponibles:
# ✅ GET liste
curl -X GET "http://localhost:8080/api/reports"
# ✅ GET par ID
curl -X GET "http://localhost:8080/api/reports/1"
# ❌ POST non disponible (405 Method Not Allowed)
curl -X POST "http://localhost:8080/api/reports" \
-H "Content-Type: application/json" \
-d '{"title": "Test"}'
# Réponse: 405 Method Not Allowed
# ❌ DELETE non disponible
curl -X DELETE "http://localhost:8080/api/reports/1"
# Réponse: 405 Method Not Allowed
Voir Aussi¶
- Annotations API Reference - Référence complète des annotations
- Configuration API Reference - Toutes les propriétés de configuration
- Guide Annotations - Guide utilisateur des annotations
- Quick Start - Premiers pas
- First Project - Tutoriel complet
Questions Fréquentes¶
Comment personnaliser le format des réponses?¶
Par défaut, SpringFlow retourne le format Page<T> de Spring Data. Pour personnaliser, implémentez votre propre controller en étendant GenericCrudController.
Les endpoints supportent-ils HATEOAS?¶
Not currently supported. HATEOAS support via Spring HATEOAS is planned for a future release.
Comment gérer les relations dans les DTOs?¶
Les relations @ManyToOne sont incluses par défaut (ID de l'entité liée). Pour inclure l'objet complet, utilisez des projections custom ou étendez le controller.
Peut-on avoir des endpoints custom en plus des CRUD?¶
Oui! Créez un controller custom qui étend GenericCrudController et ajoutez vos propres méthodes @GetMapping, @PostMapping, etc.
Comment désactiver certains endpoints (ex: DELETE)?¶
Utilisez @AutoApi(expose = Expose.CREATE_UPDATE) pour désactiver DELETE, ou Expose.READ_ONLY pour désactiver toutes les écritures.
Les validations JSR-380 sont-elles appliquées?¶
Oui! Toutes les annotations de validation (@NotNull, @Min, @Email, etc.) sont appliquées automatiquement sur POST, PUT et PATCH.
Comment tester les endpoints?¶
- Swagger UI:
http://localhost:8080/swagger-ui.html - cURL: Exemples dans cette documentation
- Postman/Insomnia: Importez le fichier OpenAPI
- Tests Spring:
@SpringBootTest+MockMvc