Vous êtes sur la page 1sur 317

Développement Full Stack

Gregory Galli
Enseignant et Chef de projet à l’Université Côte d’Azur (UCA)
Sophia Antipolis - France

1
Introduction
Développement Full Stack

2
Introduction

➢ Première partie : Backend

➢ Groovy

➢ Framework Grails

➢ API REST

➢ Sécurité

➢ Projet

3
Introduction

➢ Seconde partie: Frontend

➢ Web

➢ Responsive

➢ Progressive Web Application

➢ Angular

➢ Projet

4
Introduction

➢ Développeur Full Stack

➢ Autonomie pour la production d’application

➢ C’est quoi la « Stack »

➢ Evolution avec le temps

➢ Actuellement ?

5
C’est quoi une Stack ?

➢ Stack technique

➢ Outils et technos

➢ Production d’application

➢ Exemples

➢ MEAN Stack

➢ LAMP Stack

➢ Java / Spring, Python / Django, Ruby / Rails

6
Layers

➢ Mise à disposition d’applications

➢ Bases de données

➢ Gestion de projet
➢ Planning
➢ Segmentation
➢ Estimation

➢ Test & Documentation

➢ Backend

➢ API

➢ Frontend

7
Développeur Full Stack

➢ Extrêmement complexe

➢ Développeurs expérimentés
➢ Quantité de technologies
➢ Expérience suffisante

➢ Grande flexibilité

8
Développeur Full Stack

➢ Petits projets

➢ Capacité à gérer le projet dans son ensemble

➢ Economie de temps

➢ Gros projets

➢ Facilite la communication et la coordination des équipes

➢ Capacité à avoir une vue d’ensemble

9
Développeur Full Stack

➢ Attention

➢ Taille de la Stack

➢ Evolution de la définition

➢ Développeur MEAN Stack

➢ Développeur LAMP Stack

10
Développeur Full Stack

➢ Définition actuelle pour la « Java Stack »

➢ Spring Boot

➢ Angular / React

➢ Gestion de projet

➢ Test

➢ Déploiement

11
Développeur Full Stack – Conclusion

➢ Rester ouvert d’esprit

➢ Les définitions évoluent avec les évolutions techniques

➢ Ce qui est vrai aujourd’hui peut ne plus l’être dans un futur proche

12
Développeur Full Stack – Conclusion

➢ Développement Full Stack

➢ Backend & API : Groovy on Rails

➢ Frontend : Angular / HTML 5 / CSS 3 / JS

➢ Déploiement via les solutions embarquées ou en utilisant des services SAS

➢ Contraintes dues au format du cours

➢ Gestion de projet : Limité à l’analyse

13
Module 1:
Développement Backend
Groovy – Grails – Présentation du projet

14
Langage Groovy

Présentation

15
Groovy

➢ Créé en 2003

➢ Langage orienté Objet destine à la plateforme Java

➢ Inspirations

➢ Surcouche de Java

➢ Hérite des points forts de Java

➢ Etend Java

➢ Librairies Java

16
Groovy

➢ Syntaxe

➢ Fonctionnalités

➢ Closures

➢ Collections

➢ Typage dynamique

➢ Support natif des expressions régulières

➢ Peut être exécuté en tant que script

➢ Et bien plus …

17
Groovy – Typage dynamique

➢ Utilisation du typage dynamique ou statique


➢ mot clef “def”

➢ Vérification du type à l’exécution

➢ Avantages
➢ Flexibilité
➢ Codage plus rapide
➢ Type de la variable défini à la volée

➢ Inconvénients
➢ Rigueur
➢ Peut mener à des comportements inattendus

18
Groovy – Assertions

➢ Utilisation d’ « Assert » pour vérifier que certaines conditions sont remplies

// Définition de variable // Affiche


def subject = "Mooc" """
Caught: Assertion failed:
// Vérifie la valeur
assert subject == "Mooc" assert subject == "Mook"
| |
// Produit un msg si erreur 'Mooc' false
assert subject == "Mook" """

19
Langage Groovy – Closures
Présentation

20
Groovy – Closures

➢ Fonctions anonymes
➢ Peut prendre des arguments
➢ Retourne une valeur
➢ Peut être stocké dans une variable
➢ Pas lié au contexte
➢ Déclaration

{ [ closureParams -> ] statements }

➢ Paramètres optionnels
➢ Virgule séparatrice
➢ Flexibilité pour des opérations simples

21
Groovy – Closures – Déclaration

➢ Souvent utilisé avec le paramètre implicite « it »

// Affiche le paramètre fourni, déclaration Classique


def closureA = { def var -> println var }

// Equivalents
def closureB = { println it } // Paramètre implicite
def closureC = { it -> println it } // Paramètre nommé, pas de type défini
def closureD = { def it -> println it } // Paramètre nommé, typage dynamique
def closureE = { String it -> println it } // Paramètre nommé, variable typée

22
Groovy – Closures – Appel

➢ Différentes méthodes d’appel pour les closures


// Sans paramètre
def closure = { "mooc" }

// Peut aussi être appelé comme ça


assert closure() == "mooc"
assert closure.call() == "mooc"

// Avec paramètre
def closureWithParam = { it }

// Peut aussi être appelé comme ça


assert closureWithParam("closure") == "closure"
assert closureWithParam.call("closure") == "closure"

23
Groovy – Closures – Examples

// Retourne vrai si le paramètres est impair


def isOdd = { int i -> i % 2 != 0 }
assert isOdd(3) == true
assert isOdd(2) == false

// Retourne vrai si le paramètres est pair


def isEven = { int i -> i % 2 == 0 }
assert isEven(3) == false
assert isEven(2) == true

// Retourne la surface
def rectSurface = { int w, h -> w * h}
assert rectSurface(2, 3) == 6

24
Groovy Language –
Collections

Overview

25
Groovy – Collections

➢ Types de collection
➢ List
➢ Set
➢ Map
➢ Range

➢ Nombreuses méthodes disponibles

➢ Syntaxe simple
➢ Déclaration
➢ Utilisation

26
Groovy – Collections – List

➢ Ordonné
➢ Index débutant à 0

➢ Peut contenir des dupliquas

➢ Peut contenir des variables de types variés

➢ Déclaration & manipulation

27
Groovy – Collections – List

➢ Déclaration d’une liste

// Liste vide
List emptyList = []

// Liste d’entiers
List integerList = [1, 2, 3, 4]

// Liste mixée
List mixedList = ['Mooc', 101, [1, 2], 1.01]

28
Groovy – Collections – List

➢ Manipulation

// Déclaration d’une liste


List list = ['Paris', 'London', 'Berlin']

// Vérification des données


assert list.size() == 3
assert list.isEmpty() == false
assert list.contains('Berlin') == true

29
Groovy – Collections – List

➢ Manipulation

// Déclaration d’une nouvelle List


List list = ['Paris', 'London', 'Berlin']

// Modification de la List
list.add('Rome')
assert list == ['Paris', 'London', 'Berlin', 'Rome']
assert list.pop() == 'Paris’
assert list == ['London', 'Berlin', 'Rome']

// Cette méthode crée une nouvelle List


assert list.reverse() == ['Rome', 'Berlin', 'London']

30
Groovy – Collections – Set

➢ Non ordonné

➢ Ne peut pas contenir de dupliquas

➢ Peut contenir des variables de types variés

➢ Déclaration & manipulation

31
Groovy – Collections – Set

➢ Déclaration

// Set vide
Set emptySet = []

// Set à partir d’une List


def list = ['Paris', 'London', 'Berlin']
def listToSet = list.toSet()
def listAsSet = list as Set

32
Groovy – Collections – Map

➢ Tableau associatif

➢ Les clef sont des string par défaut

➢ Non ordonné

➢ Très pratique pour gérer des données structurées

33
Groovy – Collections – Map

➢ Déclaration

// Map vide
def emptyMap = [:]

// Map classique
Map map = [france: 'Paris', uk: 'London', germany: 'Berlin', italy: 'Rome']

34
Groovy – Collections – Range

➢ Moyen rapide pour déclarer une suite numérique

➢ Bornes inclusives par défaut

// Range classique
def inclusiveRange = 11..15
assert inclusiveRange == [11, 12, 13, 14, 15]

// Exclusions des bornes


def exclusiveRange = 11..<15
assert exclusiveRange == [11, 12, 13, 14]

35
Groovy – Collections – Les bases

➢ Méthodes à connaitre pour les Collections / Iterables

➢ plus : Combine des collections

➢ minus : Soustrait des collections

➢ each / eachWithIndex : Parcours d’une collection

➢ collect : Parcours et transforme une collection

➢ find / findAll / findIndexOf : Filtres et recherche

➢ flatten : Explicite

➢ split : Segmenter une collection

36
Groovy – Collections – Plus / Minus

➢ Combine / Soustrait des collections


➢ Crée une nouvelle collection

// Déclare une nouvelle List


List list = ['Paris', 'London', 'Berlin']

// Ces opérations créent de nouvelles listes


assert list.minus('Paris') == ['London', 'Berlin']
assert list.plus('Rome') == ['Paris', 'London', 'Berlin', 'Rome']
assert list.size() == 3

37
Groovy – Collections – Each

➢ Iterate over collection items

List list = ['Paris', 'London', 'Berlin']

// Parcours et affiche les éléments


list.each {
println it
}

// Affiche
Paris
London
Berlin

38
Groovy – Collections – Each

➢ Fonctionne avec des collections de tous les types


// Déclare une nouvelle Map
Map map = [france: 'Paris', uk: 'London', germany: 'Berlin', italy: 'Rome']

// Parcours et affiche
map.each {
println "The capital of $it.key is $it.value"
}

// Equivalent
map.each { key, value ->
println "The capital of $key is $value"
}

// Affiche
The capital of france is Paris
The capital of uk is London
The capital of germany is Berlin
The capital of italy is Rome
39
Groovy – Collections – EachWithIndex

➢ De même avec un index

// Avec un index
map.eachWithIndex { key, value, index ->
println "[$index] The capital of $key is $value"
}

// Affiche
"""
[0] The capital of france is Paris
[1] The capital of uk is London
[2] The capital of germany is Berlin
[3] The capital of italy is Rome
"""

40
Groovy – Collections – Collect

➢ Parcours les éléments et les transforme

// Collection à modifier
def newList = map.collect {
key, value ->
[ key.toUpperCase(), value ]
}

assert newList == [
['FRANCE', 'Paris'],
['UK', 'London'],
['GERMANY', 'Berlin'],
['ITALY', 'Rome']]

41
Groovy – Collections – Collect

➢ Peut prendre une nouvelle collection en paramètre

def newOtherList = []
map.collect(newOtherList) {
key, value ->
[ key.toUpperCase(), value ]
}

assert newOtherList == [
['FRANCE', 'Paris'],
['UK', 'London'],
['GERMANY', 'Berlin'],
['ITALY', 'Rome']]

42
Groovy – Collections – Find

// Déclare une nouvelle liste


def intList = [1, 2, 3, 4, 5, 6, 11, 15]

// Retourne le premier élément qui correspond


assert intList.find{ it > 5 } == 6

// Retourne tous les éléments qui correspondent


assert intList.findAll{ it > 5 } == [6, 11, 15]

// FindAll retournera une liste même si aucune correspondance


assert intList.findAll{ it > 20 } == []

43
Groovy – Collections – Other

// Déclare une nouvelle List


def multiDimList = [1, [2, 3], 4, [5, 6, [7, 8], 9]]

// Applatis une collection


def flatList = multiDimList.flatten()
assert flatList == [1, 2, 3, 4, 5, 6, 7, 8, 9]

// Retourne une liste avec deux éléments


// Le premier élément contiendra les éléments valides
// Le second contiendra les éléments non valides
assert flatList.split { it < 6} == [[1, 2, 3, 4, 5], [6, 7, 8, 9]]

44
Groovy – Syntaxe

➢ Peut être surprenant // Variables


def left = "west"
➢ On peut omettre:
def right = "east"
➢ Les parenthèses pour les méthodes
➢ Les points sur les appels successifs
// Equivalent à turn(left).then(right)
turn left then right
// Méthodes
def turn(def direction) { // Affiche
println direction """
return this west
} east
"""
def then(def direction) {
println direction
}

45
Groovy on Rails : Grails
Présentation
Grails – Contexte

➢ Créé en 2005 (Graeme Rocher)

➢ Framework Open source agile de développement full stack

➢ Basé sur le langage Groovy (Grails → Groovy on Rails (agile))

➢ Construit au dessus de Spring Boot

➢ Intégration transparente avec Java

47
Grails – Concepts & Avantages

➢ DRY : Don’t Repeat Yourself

➢ Convention over configuration

➢ Architecture orientée modèle

➢ Prototypage facile

➢ Plugins

48
Grails – Concepts & Avantages

➢ Communauté vaste et aidante

➢ Technologies de vues, principalement pour du rendu HTML / JSON

➢ Capacités asynchrones – Promesses / Event – RxJava

➢ Domain-specific languages (DSLs)


➢ Validation
➢ Requêtes
➢ Rendering

49
Grails – Concepts & Avantages

➢ GORM
➢ Hibernate (SQL)
➢ MongoDB
➢ Cassandra

➢ Serveur d’application embarqué


➢ Tomcat (Plugin)

➢ Base de données mémoires (H2)

➢ Nombreux IDE utilisables


➢ Intellij IDEA – Ultimate edition (meilleure option, licence étudiante gratuite)
➢ Eclipse
➢ Sublime

50
Grails – Architecture

51
Grails – Used by

52
Grails – Plugins

➢ Par défaut
➢ Serveurs d’application: Tomcat / Glassfish
➢ Bases de données : H2 / Hibernate
➢ Gestion des ressources web : Assets

➢ Utile
➢ Sécurité : Spring Security Core / REST
➢ Services de paiement : Paypal / Stripe / …
➢ Mails

➢ Vérifier avant d’utiliser

➢ Limiter l’usage pour éviter les problèmes de performance

53
Grails

Modélisation

54
Grails – Modélisation

➢ Principal point lorsque l’on construit une application Grails


➢ Attributs de base
➢ Contraintes
➢ Relations

➢ Scalabilité

➢ Mappings

➢ Le DSL de GORM

55
Grails – Modélisation

➢ Définition des attributs en utilisant les types de base

class BaseTypes {
byte[] grails_byte_array
String grails_string
byte grails_byte
Character grails_character
Integer grails_integer
Double grails_double
Float grails_float
Long grails_long
Date grails_date
Boolean grails_Boolean
}

➢ List complète : Documentation des types de base avec Hibernate


56
Grails – Modélisation

➢ Définition des attributs en utilisant les types de base

57
Grails – Modélisation

➢ Relations
➢ One-to-One
➢ One-to-Many
➢ Many-to-Many

class User {
String username

// One-to-One
Address address

// One-to-Many (Message) and Many-to-Many (Group)


static belongsTo = [UserGroup]
static hasMany = [messages: Message, groups: UserGroup]
}

58
Grails – Modélisation

➢ Unidirectionnel / Bidirectionnel

➢ Impact non négligeable sur les performances

➢ Bidirectionnalité quand nécessaire

➢ Propriétés ajoutées par GORM

➢ id : Long – Primary key – Auto increment

➢ version : Long – Used to maintain data consistency

59
Grails
Hibernate, GORM & Requêtes

60
Grails – Hibernate

➢ Framework de persistance Open Source

➢ Utilisable dans tous les environnements (web / classique)

➢ Remplace une couche d’accès aux données (Data Access Layer)

➢ Fourni des méthodes d’accès de haut niveau

➢ Utilisable avec la pluspart des SGBD relationnels

➢ Utilisé dans le cadre du développement Grails via GORM

61
Grails – GORM

➢ Grails Object Relational Mapping

➢ Framework d’accès aux données


➢ Méthodes d’accès rapide

➢ GORM est utilisable avec …


➢ Les SGBD relationnels (Hibernate)
➢ Certains SGBD NoSQL (MongoDB / Cassandra)
➢…

➢ Fourni l’illusion d’une base de données orientée objet

➢ Correspondance objet - relationnel

62
Grails – Hibernate / GORM – Avantages

➢ On accède a des objets à la place des tables

➢ Mise en place et usage simpl


➢ Pas besoin de définir les tables / colonnes
➢ Utilisation des « dynamic finders » pour le prototypage ou les requêtes simples

➢ Gestion des transactions

➢ Syntaxe unique peu importe le SGBD


➢ Transition facile si migration

63
Grails – Hibernate / GORM – Inconvénients

➢ Peut être délicat dans des projets complexes

➢ Ajoute un intermédiaire, diminue les performances

➢ Abstraction

➢ Ne veux pas dire que l’on peut ne pas comprendre ce qu’il se passe

➢ Sans compréhension globale, blocage inévitables

64
Grails – GORM – Requêtes

➢ Dynamic Finders

➢ Where Queries
Request
Performances
complexity
➢ Criteria Queries

➢ Hibernate Query Language (HQL)

65
Conclusion

66
Conclusion

➢ Couverture des bases

➢ Groovy

➢ Grails

➢ Pour aller plus loin : Approfondissement


➢ Structure de projet

➢ Modélisation

➢ Gestion des données

➢ Sécurité

➢ Se lancer dans le développement backend


67
Grails – Configuration de
l’environnement
Java – Framework Grails – Intellij IDEA Ultimate

68
Module 2:
Développement Backend

Structure de projet, Configuration, Modélisation, Associations, Contraintes et bien


plus …

69
Grails – Plan

➢ Structure de projet – Assets – Configurations

➢ Modélisation avancée
➢ Héritage
➢ Associations
➢ Contraintes
➢ Mapping
➢ lazy loading

➢ Contrôleurs
➢ Data Handling
➢ Services
➢ GORM & Hibernate Querying

70
Grails – Structure de projet
Structure de projet et plugin « Asset »

71
Grails – Structure de projet

➢ Convention over configuration


➢ Nom et emplacement des fichiers

➢ grails-app : dossier source


➢ assets : ressources web, géré par le plugin Asset
➢ conf : configurations sources
➢ Contrôleurs : controlleurs
➢ domain : modèle
➢ i18n : fichiers d’internationnalisation
➢ init : contient BootStrap.groovy file (données d’initialisation)
➢ services : couche de service
➢ taglib : explicite
➢ utils : utilitaires
➢ views : contient les GSP et les JSON views
➢src / …

72
Grails – Structure de projet

➢ Convention over configuration


➢ Nom et emplacement des fichiers

➢ grails-app : dossier source


➢ […]
➢ src / integration-test : explicite
➢ src / main : sources groovy
➢ src / test : tests unitaires

73
Grails – Plugin Asset

➢ Gère et traite les ressources statiques

➢ Optimise et minifie les fichiers CSS et JS

➢ Balises personnalisée pour la gestion des ressources web

➢ Peut être utilisé pour compiler des ressources


➢ LESS
➢ SASS
➢…

74
Grails – Plugin Asset

➢ Fichier par défaut contenant des directives


➢ Structure par défaut
➢ grails-app/assets/javascript
➢ grails-app/assets/images
➢ grails-app/assets/stylesheets

75
Grails – Plugin Asset – Sert les ressources

➢ Servis par le server d’application dans la configuration de développement


➢ Doit être externalisé pour la mise en production
➢ Server web dédié
➢ CDN
# grails-app/conf/application.yml
environments:
# for specific environment
production:
grails:
# specify
assets:
# the base url
url: http://cdn.Exemple.com/
# the base path
storagePath: /var/www/dedicated/web/server

76
Grails – Plugin Asset – Taglibs

➢ Utilisation des Taglibs pour créer des liens propres

<head>
%{-- Javascript / CSS inclusion dans la page--}%
<asset:javascript src="application.js"/>
<asset:stylesheet src="application.css"/>
</head>
<body>
%{-- Référence simple à une resource --}%
<asset:image src="logo.png"/>
</body>

77
Grails – Structure de projet
– Configuration

Configuration, Data Sources, URL Mappings, Bootstrap & Plugins

78
Grails – Configuration

➢ Pas obligatoire
➢ Principalement des surcharges
➢ Configuration de build → build.gradle
➢ Configuration pour l’exécution → grails-app/conf/application.yml
➢ Peut être externalisé

# fichier : grails-app/conf/application.yml
server:
# Changement du port de déploiement
port: 8082
# Changement du context path
contextPath: /myProjetName

79
Grails – Data Sources

➢ Contient la configuration des sources de données


➢ Configuration par défaut utilisable
➢ Configuration par environnement
➢ Base par défaut : H2
➢ Changement de base
➢ Fournir un nouveau driver (dépendances/ JAR)
➢ Changer le driverClassName
➢ Changer le username & password si besoin
➢ Editer l’url
➢ Redémarrer le projet
➢ C’est fait !

80
Grails – URL Mappings

➢ Défini dans grails-app/controller/package/UrlMappings.groovy

➢Mapping URL
➢ Contrôleurs class UrlMappings {
➢ Actions static mappings = {
➢ Ressources REST "/$controller/$action?/$id?(.$format)?"{
constraints {
// apply Contraintes here
➢ Mapping par défaut }
➢ Pas vraiment une convention }
➢ Modifiable
"/"(view:"/index")
"500"(view:'/error')
"404"(view:'/notFound')
}
}
81
Grails – URL Mappings

➢ Redirection sur vue statique


"/"(view:"/index")
"500"(view:'/error')
"404"(view:'/notFound‘)

➢ Redirection vers un contrôleur / une action


// default action : index
"/"(controller: "main")
// equivalent to
"/"(controller: "main", action: "index")

➢ Redirection sur une ressource REST

"/users"(resources: "user")

82
Grails – URL Mappings

➢ Autres possibilités
➢ Variables prédéfinies
➢ Variables optionnelles
➢ Expressions régulières

// mapping des parties de l’url sur les variables


// validera l’url: /promo/product/107
"/$category/product/$id"(controller: "product")

// utilisation du ? pour rendre les paramètres optionnels


// validera l’url : /promo/product/107
// ainsi que : /promo/product
"/$category/product/$id?"(controller: "product")

83
Grails – URL Mappings

➢ Autres possibilités
➢ Variables prédéfinies
➢ Variables optionnelles
➢ Expressions régulières

// utilisation des expressions régulières


// validera : /promo/product/107
// ne validera pas : /promo/product/abc
"/$category/product/$id"
{
controller = "product"
constraints{
id(matches: /\d+/)
}
}

84
Grails – Bootstrap

➢ Situé dans le répertoire « init »


➢ Contient deux « closures »
➢ “init” appelé au lancement du serveur d’application
➢ “destroy” est appelé quand
➢ L’instance de la servlet est « démontée »
➢ Prudence sur ce point
➢ Principalement utilisé pour définir les données de base
➢ Définition des utilisateurs / rôles…
➢ Très utile pendant la phase de développement
➢ Données perdues au redémarrage
➢ Réinitialisation des données grâce au Bootstrap
➢ Utilisation des options de la méthode save
➢ Flush : demande la purge du contexte de persistance
➢ FailOnError : arrête le serveur d’application si l’opération n’est pas réussie

85
Grails – Plugins

➢ Définition dans le fichier build.gradle


➢ Enorme base de plugins
➢ Utiliser avec modération
➢ Plugins par défaut
➢ Hibernate
➢ Assets
➢ Tomcat
➢ …
➢ Utiles
➢ Spring Security Core
➢ Spring Security REST
➢ Connecteurs
➢ …

86
Grails – Modélisation

Focus sur la Modélisation, Héritage, Associations, Contraintes & Mapping

87
Grails – Modélisation – Héritage

➢ Eviter autant que possible


➢ Problèmes de performances
➢ Comportement par défaut
➢ Rassemble toutes les propriétés dans la même table
➢ Empêche de forcer un champs à « non null »

class Client extends User { class User { class Prospect extends User {
String clientRef String username String prospectRef
} } }

88
Grails – Modélisation – Héritage

new User(username:"user", address: new Address()).save()


new Client(username:"client", clientRef: "clientRef", address: new Address()).save()
new Prospect(username:"prospect", prospectRef: "prospectRef", address: new Address()).save()

89
Grails – Modélisation – Héritage

➢ Les propriétés qui ne correspondent pas sont à « null »


➢ Colonne « Class » garder trace dans l’héritage
➢ Comportement par défaut : table-per-hierarchy
➢ Surcharge : table-per-subclass via le DSL de mapping

90
Grails – Modélisation – Héritage

class Client extends User { class User { class Prospect extends User {
String clientRef String username String prospectRef
} }
static mapping = {
tablePerHierarchy false
}

91
Grails – Modélisation – Héritage

➢ Pas de colonne inutile


➢ Economise de l’espace disque

➢ Chaque requête va engendrer un « join »


➢ Coût d’exécution supérieur
➢ Coût en accès disque supérieur

➢ Pas de « meilleur choix » systématique → décider en fonction du contexte

92
Grails – Modélisation – Héritage

➢ Transparent sur la manipulation d’objets

// Récupère les utilisateurs et affiche en JSON


render User.list() as JSON

93
Grails – Associations

Focus sur la Modélisation, Héritage, Associations, Contraintes & Mapping

94
Grails – Modélisation – Associations

➢ Définition des intéractions entre les classes

➢ Unidirectionnel par défaut

➢ Solutions possibles
➢ Many-to-one
➢ One-to-One
➢ One-to-Many
➢ Many-to-Many

➢ Cas complexes
➢ Plusieurs propriétés d’un même type
➢ Auto-références
➢ Gérer les suppositions « fausses » de GORM

95
Grails – Associations

Many-to-One & One-to-One

96
Grails – Associations – Many-to-One

➢ Cas le plus évident // Book.groovy


class Book {
String title
➢ Unidirectionnel Author author
}
➢ Plusieurs Livres peuvent référencer le même Auteur // Author.groovy
class Author {
➢ Un Livre référence une instance d’Auteur String name
}

97
Grails – Associations – Many-to-One

➢ Création d’un nouvel objet dans avec une relation Many-to-one

// Crée un nouveau Livre avec un nouvel Auteur


new Book(title: "title", author: new Author(name: "author's name")).save()

// Crée un nouveau Livre avec un Auteur existant


new Book(title: "title", author: Author.get(1)).save()

// Crée un Auteur ainsi que deux Livres


def authorInstance = new Author(name: "new author").save()
new Book(title: "title", author: authorInstance).save()
new Book(title: "other title", author: authorInstance).save()

98
Grails – Associations – One-to-One

➢ « Sorte de » One-to-one // User.groovy


class User {
➢ L’Adresse appartient à l’Utilisateur String username
Address address
➢ Se comporte comme un One-to-one
}

➢ Conséquences du « BelongsTo » // Address.groovy


➢ Dépendance forte entre Adresse et Utilisateur class Address {
➢ Cascade tout de l’Utilisateur vers L’Adresse static belongsTo = [user: User]
➢ L’Adresse ne peut pas exister « seule » }

99
Grails – Associations – One-to-One

// User.groovy
class User {
String username
Address address
}

// Address.groovy
class Address {
static belongsTo = [user: User]
}
// Sauvegarde user ET address
def userInstance = new User(username: "username")
userInstance.address = new Address()
userInstance.save()

// Efface user ET address associée


userInstance.delete()
100
Grails – Associations – One-to-One

➢ Véritable One-to-one // User.groovy


class User {
String username
➢ Clef étrangère du côté « faible » static hasOne = [address: Address]
de la relation (Address) }

// Address.groovy
➢ hasOne toujours bidirectionnel class Address {
// Référence simple
// pas de cascade sur delete
➢ Sans « belongsTo » User user
➢ Cascade les sauvegardes et updates
}

➢ Avec « belongsTo »
➢ Cascade delete en plus

101
Grails – Associations – One-to-One

➢ Sauvegarder un nouvel objet avec une relation One-to-one

// Sauvegarde un nouvel User avec une nouvelle Address


// Pas besoin de définir le lien manuellement
new User(username: "username", address: new Address()).save()

// L’Utilisateur ne sera pas créé, Adresse obligatoire


new User(username: "username").save()

// La classe Address a une reference vers User, impossible de créer sans lien
new Address().save()

102
Grails – Associations

One-to-Many

103
Grails – Associations – One-to-Many

➢ Un User a plusieurs Messages // User.groovy


class User {
String username
➢ Créera une table de jointure static hasMany = [messages: Message]
}

// Message.groovy
class Message {
➢ Nom de la table et des colonnes String content
}
personnalisable avec « mappedBy »
➢ Comportement de cascade
➢ Cascade sauvegardes et mises à jour
➢ Cascade suppression (si belongsTo)
➢ La propriété « message » sera un « Set »
➢ Peut être surchargé
104
Grails – Associations – One-to-Many

// Instancie un nouvel User


def userInstance = new User(username: "username")

// Ajouter un Message à un User


userInstance.addToMessages(new Message(content: "message content"))

// Ajout de plusieurs Messages


["message","other message", "another message", "..."].each {
userInstance.addToMessages(new Message(content: it))
}

// Sauvegarde l’User ainsi que les Messages associés


userInstance.save()

// Supprime l’Utilisateur sans supprimer les Messages (pas de belongsTo)


userInstance.delete()
105
Grails – Associations – One-to-Many

➢ Stratégie de « fetching » // User.groovy


➢ Défaut : Lazy class User {
➢ Peut être surchargé String username
static hasMany = [messages: Message]
}
➢ « Eager fetching »
➢ Attention // Message.groovy
➢ Relations cycliques class Message {
➢ Uniquement si besoin String content
}

➢ Les collections ne sont pas intégralement chargé immédiatement

106
Grails – Associations – One-to-Many

➢ Récupérer un User récupèrera // Récupère un User sur son ID (1)


une liste de références de def userInstance = User.get(1)
// On a uniquement les références des Messages
Messages
// Itère sur les Messages du User
➢ Pendant le parcours de la // va déclencher la récupération des Messages
collection, GORM ira récupérer userInstance.messages.each{
// Affiche le contenu du Message dans la console
les Messages println it.content
}
➢ « messages » est un Set
➢ Non ordonné
➢ Peut être surchargé avec le type « List »

107
Grails – Associations – One-to-Many

➢ Effacer le côté « Many » de l’association


➢ Déclarer le côté « Many » avec un « belongsTo » du côté « One »
➢ Déclarer un comportement de cascade explicite
// Instancie un User
def userInstance = new User(username: "username")

// Ajoute plusieurs messages


["message","other message", "another message", "..."].each {
userInstance.addToMessages(new Message(content: it))
}

// Sauvegarde userInstance va créer un User et des Messages


userInstance.save()

// Va effacer l’User ainsi que les Messages associés


userInstance.delete(flush:true)
108
Grails – Associations – One-to-Many

➢ Effacer le côté « Many » de l’association


➢ Déclarer le côté « Many » avec un « belongsTo » du côté « One » (1)
➢ Déclarer un comportement de cascade explicite (2)

➢ Exemple pour le cas 1.

class User { class Message {


String username String content
static hasMany = [messages: Message] static belongsTo = [user: User]
} }

109
Grails – Associations – One-to-Many

➢ Effacer le côté « Many » de l’association


➢ Déclarer le côté « Many » avec un « belongsTo » du côté « One » (1)
➢ Déclarer un comportement de cascade explicite (2)

➢ Exemple pour le cas 2.

class User {
String username
static hasMany = [messages: Message] class Message {
static mapping = { String content
messages cascade: 'all-delete-orphan' }
}
}

110
Grails – Associations – One-to-Many

➢ « belongsTo » du côté « Many »


➢ Change la structure de la base de donnée

➢ Sans « belongsTo »
➢ Table d’association

111
Grails – Associations – One-to-Many

➢ « belongsTo » du côté « Many »


➢ Change la structure de la base de donnée

➢ Avec « belongsTo »
➢ Pas de table d’association
➢ Le côté « Many » référence le côté « One »

112
Grails – Associations

Many-to-Many

113
Grails – Associations – Many-to-Many

➢ Gestion délicate avec les ORM

➢ Se défini avec un« hasMany » de chaque côté

➢ On doit préciser le côté faible de la relation avec un « belongsTo »

// Author.groovy
// Book.groovy class Author {
class Book { String name
String title
static hasMany = [books: Book]
static hasMany = [authors: Author] // Author est le côté faible
} static belongsTo = Book
}

114
Grails – Associations – Many-to-Many

➢ Géré comme un « One-to-Many » au niveau de la base de donnée

➢ Le côté « fort » de la relation est en charge des opérations de persistance


➢ On ne peut pas avoir une cascade des « save » et « update » depuis le côté faible

115
Grails – Associations – Many-to-Many

// Sauvegardera un Book ainsi que les Authors liés (côté fort)


new Book(title: "Art and Science of Big Data")
.addToAuthors(new Author(name: "Serge Miranda"))
.addToAuthors(new Author(name: "Robin Girard"))
.addToAuthors(new Author(name: "Benjamin Renaut"))
.save()

// Sauvegardera uniquement l’Author(côté faible)


new Author(name: "Serge Miranda")
.addToBooks(new Book(title: "Art and Science of Big Data"))
.addToBooks(new Book(title: "Relational databases"))
.save()

116
Grails – Associations – Sets & Lists

➢ Au niveau du langage « One-to-Many » est un Set


➢ Non ordonné
➢ Ne peut pas contenir du dupliquas
➢ Peut être couteux avec des collections importantes
➢ Dans certains cas on évitera d’utiliser ces déclarations pour les gérer manuellement

➢ Un Set peut être surchargé en List


➢ Ordonné
➢ Conserve malgré tout la contrainte d’unicité
➢ Gestion automatique des indexs
➢ Ajout d’une nouvelle colonne dans la base de donnée

117
Grails – Modélisation &
Contraintes

118
Grails – Modélisation – Contraintes

➢ Déclaration des contraintes dans les classes du modèle


➢ Contraintes niveau base
➢ Contraintes niveau code

➢ Via des propriétés


➢ Nom de la propriété concernée
➢ Les paramètres de contrainte

➢ Défaut : Toutes les propriétés sont « nullable: false »

➢ Rend le développement plus sûr

119
Grails – Modélisation – Contraintes

120
Grails – Modélisation – Contraintes

121
Grails – Modélisation – Contraintes

class User {
String username
String email
static hasOne = [address:Address]
static hasMany = [messages: Message]

static constraints = {
// le username ne pourra pas être null ou vide et aura entre 5 et 15 chars
username blank: false, nullable: false, size: 5..15
// email non vide, non null et format valide et unique
email blank: false, nullable: false, email: true, unique: true
// address devra être définie pour un User
address nullable: false
}
}

122
Grails – Modélisation
Mapping

123
Grails – Modélisation – Mapping

➢ Personnalisation du mapping class User {


String username
➢ Personnalisation String email
➢ Nom des tables static hasOne = [address:Address]
➢ Nom des tables de jointure static hasMany = [messages: Message]
➢ Nom des champs static mapping = {
➢ Gestion de la version // Custom table name
➢ Lazy / Eager fetching table 'frontend_users'
➢ Stratégie de gestion du cache // Prevent version handling
➢ Auto timestamping version false
➢ dateCreated // Custom column name
➢ lastUpdated username column: 'user_name'
➢ et bien plus … // Eager fetching for messages
messages lazy: false
}
}
124
Grails – Modélisation – Mapping

➢ Personnalisation du mapping
class User {
➢ Personnalisation […]
➢ Nom des tables static hasMany = [messages: Message]
➢ Nom des tables de jointure String description
➢ Nom des champs Date dateCreated
➢ Gestion de la version Date lastUpdated
➢ Lazy / Eager fetching static mapping = {
➢ Stratégie de gestion du cache // Disable autotimestamping
➢ Auto timestamping autoTimestamp false
➢ dateCreated // Force field type to 'text'
➢ lastUpdated description type: 'text'
➢ et bien plus … // Custom join table
messages joinTable: [name : 'user_messages',
key : 'user_id',
column: 'message_id']
} 125
Grails – Modélisation – Mapping

➢ Comportement de Cascade
➢ save-update : cascade les sauvegardes et mises à jour
➢ delete : cascade uniquement les suppressions
➢ all : cascade tout
➢ all-delete-orphan :cascade tout et tente de supprimer les références orphelines
➢ autres cas : http://gorm.grails.org/6.0.x/hibernate/manual/#customCascadeBehaviour

➢ Par défaut
class User {
➢ hasMany cascade save-update
static hasMany = [messages: Message]
➢ belongsTo cascade all-delete-orphan
static mapping = {
// Define cascade behaviour
messages cascade: 'all-delete-orphan'
}
}

126
Grails – Contrôleurs

Structure & scopes

127
Contrôleurs – Notions de base

➢ Gère les requêtes

➢ Répond à l’appelant

➢ Convention / configuration par défaut


➢ Chaque action est liée à une URL (cf. UrlMappings.groovy)
➢ Action par défaut définissable dans le contrôleur
// default overridable value
static defaultAction = "list"

➢ Si action « index » présente, sera utilisée par défaut

128
Contrôleurs – Scopes (Objets)

➢ Série de variables contenant des données


➢ Accessible dans toutes les méthodes de contrôleurs
➢ Accès direct sur le nom du scope en question
➢ 5 Scopes disponibles
➢ servletContext
➢ session
➢ request
➢ params
➢ flash

129
Contrôleurs – Scopes (Objets)

➢ servletContext
➢ Instance de ServletContext
➢ Aussi appellé « Application scope »
➢ Disponible de manière global sur l’application
➢ Un contexte par application
➢ session
➢ request
➢ params
➢ flash

130
Contrôleurs – Scopes (Objets)

➢ servletContext
➢ session
➢ Instance de HttpSession
➢ Utilisé pour stocker de l’information associée à un utilisateur
➢ Session HTTP
➢ request
➢ params
➢ flash

131
Contrôleurs – Scopes (Objets)

➢ servletContext
➢ session
➢ request
➢ Utilisé pour stocké de l’information sur la requête courante
➢ Instance de HttpServletRequest
➢ Contient toutes les informations de la requête
➢ Cookies
➢ Format
➢ Locales
➢ Informations de sécurité
➢ Request data
➢ Host information
➢ …
➢ params
➢ flash

132
Contrôleurs – Scopes (Objets)

➢ servletContext
➢ session
➢ request
➢ params
➢ Tableau associatif multi dimensionnel
➢ Même portée que la requête
➢ Sorte d’équivalent du scope « request » proposé sous forme de Map (et plus synthétique)
➢ Contient les paramètres des requêtes
➢ Utilisation fréquente pour la récupération des paramètres de requête
➢ Très utilisé pour le « data binding »
➢ flash

133
Contrôleurs – Scopes (Objets)

➢ servletContext
➢ session
➢ request
➢ params
➢ Url appelée
http://myserver.url/test?f_name=first_name&l_name=last_name
➢ Contenu du scope « params »

➢ flash
134
Contrôleurs – Scopes (Objets)

➢ servletContext
➢ session
➢ request
➢ params
➢ flash
➢ Scope temporaire
➢ Information disponible pour la requête courante AINSI QUE la suivante
➢ Vidé après la prochaine requête
➢ Très utile pour certains cas comme par exemple passer des données après un redirect

135
Contrôleurs – Scope (portée)

➢ Défini la portée de nos contrôleurs

➢ Portée par défaut « prototype »

➢ Peut être surchargé de manière globale (application.yml)

➢ 3 scopes disponibles
➢ prototype
➢ session
➢ singleton

136
Contrôleurs – Scope (range/reach)

➢ prototype
➢ Une nouvelle instance est créée pour chaque requête

➢ session
➢ Une nouvelle instance est créée pour chaque session utilisateur

➢ singleton
➢ Une instance unique et globale est créée et partagée
➢ Attention : pas de définition de propriété car partagé

137
Contrôleurs – Intercepteurs

➢ Basé sur les contrôleurs

➢ Utilisé pour déclencher des actions


➢ Avant / après l’appel de contrôleurs / actions
➢ Après le rendu de vues

➢ Peut inclure / exclure certaines contrôleurs en se basant sur leurs noms


➢ Possibilité d’utiliser des expressions régulières

➢ Possibilité de définir une notion d’ordre / priorité

➢ Peut être utilisé pour une implémentation très basique d’une sécurité

138
Grails – Contrôleurs –
Gestion de la data

Data binding & Response handling

139
Data handling

➢ Data binding
➢ Transition entre le web / les formulaires vers des données dans le monde Groovy
➢ Validation des données
➢ Sécurité

➢ Responses
➢ Comment retourner l’information à un utilisateur
➢ Les formats
➢ Converters
➢ Marshallers
➢ Méthodes à utiliser (respond…)
➢ JSON Builder

140
Data handling – Binding

➢ Requête à un serveur
➢ Formulaires
➢ Invocation HTTP

➢ Convertir une String / un nombre contenu dans la requête dans un type


correspondant aux propriétés
// Données dans une Map
class User { def map = [username: "username", age: "50"]
String username // Injection via le constructeur
Integer age def newUserInstance = new User(map)
} // Sauvegarde de l’User créé
newUserInstance.save()

141
Data handling – Binding

➢ Dans le cas des relations ?


➢ Référence simple

class User {
class Address {
String username
String address
Integer age
User user
static hasOne = [address: Address]
}
}

def map = [username: "username", age: "50", address: [address: "3 Groovy Place"]]

142
Data handling – Binding

➢ Dans le cas des relations ?


➢ One-to-Many

class User {
String username class Message {
Integer age String content
static hasOne = [address: Address] static belongsTo = [user: User]
static hasMany = [messages: Message] }
}

def map = [username: "username", age: "50", address: [address: "3 Groovy Place"],
"messages[0]": [content: "Message content"],
"messages[1]": [content: "Other message content"]]

143
Data handling – Binding

➢ Can be used to update data


➢ Slightly different syntax

// Chargement du User
def userInstance = User.get(1)
// Donnée dans une Map
def map = [ username: "username", age: "50", address: [address: "3 Groovy Place"],
"messages[0]": [content: "Message content"],
"messages[1]": [content: "Other message content"]]
// Liaison des données au modèle de cette manière
userInstance.properties = map
// Sauvegarde l’User
userInstance.save(flush: true)

144
Data handling – Binding

➢ Bien d’autres possibilités / cas

➢ Documentation très riche sur le sujet

➢ Possibilité de contourner ces mécanismes manuellement


➢ Récupération de la donnée de la requête
➢ Vérification du format
➢ Mise à jour des propriétés

145
Data handling – Response

➢ Model and views

➢ Méthode « respond »

➢ Méthode « withFormat »

➢ Builders

➢ Marshallers

146
Data handling – Response - Model and views

➢ Le « model » du point de vue des contrôleurs est une Map

➢ Injection dans les vues

➢ Plusieurs méthodes

➢ Choisir la vue
➢ Via les conventions
➢ Choix explicite

147
Data handling – Response - Model and views

➢ Déclare et retourne une Map


def show()
{
// Retourne l'instance de User dans la variable "user"
[user: User.get(params.id)]
}

➢ Convention pour le choix de la vue associée

/views/controllerName/actionName.gsp

148
Data handling – Response - Model and views

➢ Exemple : Appel - http://myserver.url/user/show?id=1

// UserController.groovy
def show()
{
// Retourne l'instance de User dans la variable "user"
[user: User.get(params.id)]
}

➢ Sera traduit par


➢ Donnée : Instance d’User dans une variable « user »
➢ Vue : Utilisera la vue définie sous

/views/user/show.gsp

149
Data handling – Response - Model and views

➢ Autres méthodes
➢ return ModelAndView
➢ méthode « render »

// Retourne l'instance de User dans la variable "user"


[user: User.get(params.id)]

// Strict équivalent
return new ModelAndView("/user/show", [user: User.get(params.id)])

// Aussi équivalent
render(view: "/user/show", model: [user: User.get(params.id)])

150
Data handling – Response - Respond

➢ Moyen préféré pour retourner de la donnée

➢ Gestion autonome de la négociation de contenu


➢ Header HTTP « Accept »
➢ Paramètres de la requête
➢ Extension de l’URL

➢ Recherche du « mime type » correspondant

➢ Tente de rendre la réponse dans le format « le plus approprié »

151
Content negotiation

➢ Types MIME types:


all: '*/*'
atom: application/atom+xml
css: text/css
➢ Défini dans application.yml csv: text/csv
form: application/x-www-form-urlencoded
html:
- text/html
- application/xhtml+xml
js: text/javascript
json:
- application/json
- text/json
multipartForm: multipart/form-data
pdf: application/pdf
rss: application/rss+xml
text: text/plain
hal:
- application/hal+json
- application/hal+xml
xml:
- text/xml
- application/xml

152
Data handling – Response - Respond

➢ Exemple

class User {
String username
Integer age
static hasOne = [address: Address]
static hasMany = [messages: Message]
}

// UserController.groovy
def list()
{
// Gestion de la négociation de contenu
// Retourne la donnée dans le format “le plus approprié”
respond User.list()
}

153
Data handling – Response - Respond

➢ Appel - http://myserver.url/user/list

➢ A partir d’un browser


➢ Tentera de retourner la page web correspondante (erreur 500 si la page n’existe pas)

➢ A partir d’une requête HTTP (avec cURL par exemple) sans plus de précision
donnnera le même résultat
curl http://myserver.url/user/list -I

➢ Retournera une erreur HTTP/1.1 500 si la page n’existe pas


➢ Retournera le contenu HTML de la page si elle existe (convention !)

154
Data handling – Response - Respond

➢ Même scénario avec le header « Accept » défini


curl http://myserver.url/user/list -H "Accept: application/json“

➢ Retournera HTTP/1.1 200, tout va bien

[
{
"id":1,
"address":{"id":1},
"age":25,
"username":"username-5",
"messages":[{"id":1}]
}
]

155
Data handling – Response - Respond

➢ Possibilité alternative de définir le format dans l’URL


curl http://myserver.url/user/list.JSON

➢ Retournera HTTP/1.1 200, tout va bien

[
{
"id":1,
"address":{"id":1},
"age":25,
"username":"username-5",
"messages":[{"id":1}]
}
]

156
Data handling – Response - WithFormat

➢ Les objectifs sont les mêmes


➢ « Pas de gestion autonome » de la négociation de contenu
➢ Possibilité de fournir du contenu spécifique pour chaque type demandé
➢ Très utile quand on souhaite rendre un contenu différent en fonction du format
➢ « Wildcard » pour gérer « tous les autres cas »
// Récupère la liste des User
def userList = User.list()

// Rendu spécifique pour chaque “format”


withFormat {
html {render (template: 'user', model:[userList: userList])}
json {render userList as JSON}
'*' {render userList as XML}
}

157
Data handling – Response – Builder

➢ Utile pour construire des réponses sur mesure

➢ Pour des besoins ponctuels uniquement

➢ Pas de possibilité d’appliquer à un niveau global


➢ Marshallers

➢ Très verbeux et difficile à lire

158
Data handling – Response – Builder

def userList = User.list()


def builder = new JsonBuilder()
// Construction d’un JSON
def result = builder.users {
// On itère sur les User de la liste
userList.each {
// On crée un nouveau noeud pour chaque
User userInstance -> user {
// Définition des propriétés
id userInstance.id
address (id: userInstance.address.id)
age userInstance.age
username userInstance.username
// Possibilité de travailler sur les associations
messages {
userInstance.messages.each {
Message messageInstance -> message(id: messageInstance.id)
}
}
}
}
}
// Rend le contenu
render builder.toPrettyString()

159
Data handling – Response – Marshallers

➢ Très utile si besoin de personnaliser la sérialisation d’objets


➢ Même syntaxe que les « Builders »
➢ Tout aussi verbeux et difficile à lire
➢ Possibilité de faire des déclarations globales
➢ Peut être déclaré dans
➢ Le Bootstrap, mais pas idéal
➢ Dans les sources Groovy (plus propre)
➢ Certains plugins peuvent aider à garder un projet plus « propre »
➢ Besoin de créer des Marshallers pour
➢ Les classes du modèle
➢ Les collections de classes du modèle

160
Data handling – Response – Marshallers

// Enregistrement du marshaller sur le modèle


JSON.registerObjectMarshaller(User)
{
def result = [:]
result.id = it.id
result.age = it.age
result.username = it.username
// On devrait aussi declarer des marshallers pour les classes
// Address & Message
result.address = it.address
result.messages = it.messages
return result
}

161
Grails – Services

Notions de base

162
Services – Notions de base

➢ Devrait contenir tout le code métier


➢ Eviter le code métier dans les contrôleurs

➢ Situé dans « grails-app/services »

➢ Convention : Le nom de la classe doit se terminer avec « Service »

➢ Souvent en charge des opérations de persistance


➢ Devraient être transactionnels dans la plupart des cas
➢ Comportement transactionnel par défaut avant Grails 3.1
➢ A la demande depuis
➢ Annotations
➢ méthode « withTransaction »

163
Rappel – Transactions – A.C.I.D.

➢L'environnement transactionnel doit respecter les règles : Propriétés A.C.I.D.

➢ A pour Atomicité (Atomicity)

➢ Principe le plus fondamental des transactions

➢ Soit tout, soit rien n'est sauvegardé

➢ On y parvient généralement en utilisant les mots-clés BEGIN, COMMIT et ROLLBACK.


➢ Une fois qu'une transaction est lancée (BEGIN)
➢ Tout sera exécuté et sauvegardé (COMMIT)
➢ Ou tout sera ramené à son état initial (ROLLBACK).

➢ Dans l'environnement Grails, les exceptions lancées dans une transaction peuvent
déclencher un retour en arrière.
➢ Le retour en arrière peut se produire lorsque l'événement n'est pas explicitement appelé.
164
Rappel – Transactions – A.C.I.D.

➢ C pour Cohérence
➢ L'état de la base de données avant et après la transaction doit respecter l'unicité, les clés
étrangères ou d'autres contraintes.
➢ La cohérence doit être préservée
➢ Quelques exceptions pour l'état " au milieu ".

➢ I pour Isolation
➢ Problématique la plus difficile
➢ Définir comment gérer les lectures / mises à jour / suppressions concurrentes sur une base
de données.
➢ Les données que je vais mettre à jour peuvent avoir changé entre le moment où je les ai lues et le
moment où je vais les mettre à jour.
➢ Ecrire les données sans vérifier si les modifications précédentes ont été annulées.
➢ Revenir sur les changements que j'allais faire annule une action qui aurait dû être exécutée.
➢ Aller au milieu peut entraîner une corruption des données dans la base de données.

165
Rappel – Transactions – A.C.I.D.

➢I pour Isolation Transation A Transation B


Retrieve User « Bob »
➢ Plusieurs mécanismes pour gérer cela /
From database
➢ Niveau d'isolation personnalisable
➢ Dépend du SGBD Retrieve User « Bob »
/
From database

➢ Inconvénient : Deadlock Set User Score (12) Update User Score


to 14 Score = Score (12) + 1

➢ Force le SGBD à annuler "certaines" transactions. Commit changes /

User score = 14 Commit changes

/ User score = 13

166
Rappel – Transactions – A.C.I.D.

➢D pour Durabilité
➢ Si un commit est validé par le SGBD, l'intégrité des données doit être maintenue.
➢ Même si
➢ Une erreur se produit
➢ Le serveur est arrêté
➢ Le serveur doit s'assurer que les données ne sont pas perdues

➢ Conclusion
➢ Les transactions sont essentielles à l'intégrité des données
➢ Les transactions peuvent entraîner des « inconvénients » qui doivent être contournés par
de bonnes pratiques de développement.

167
Services – @Transactional

➢ Possibilité de définir un service comme " Transactionnel " avec cette Annotation

➢ On peut affiner le comportement transactionnel


@Transactional
➢ option readOnly class UserService {
def create() { }
➢ Peut être surchargé au niveau de la @ReadOnly
méthode def list() { }

@Transactional(readOnly = true)
➢ Rollback déclenché sur le lancement def get() { }
d'une RuntimeException
@NotTransactional
def doThis() { }
}

168
Services – withTransaction

➢ Transaction programmatique

➢ Pas d'annotation nécessaire

class UserService {
def create(String name, Integer age) {
User.withTransaction {
status ->
new User(name: name, age: age).save()
}
}
}

169
Services – withTransaction

➢Rollback programmatique à l'aide de TransactionStatus

➢ La méthode setRollbackOnly définit l'état de la transaction comme "rollback-


only"
def edit(Long id, String name, Integer age) {
User.withTransaction {
status ->
def userInstance = User.get(id)
userInstance.name = name
userInstance.age = age
// If conditions are not met, rollback
if (age < 18)
status.setRollbackOnly()
else userInstance.save()
}
}
170
Services – Scopes

➢ Comme Contrôleurs, les ont Services a des "scopes"

➢ Scopes par défaut


➢ Singleton - Une seule instance globale du service

➢ Scopes disponibles
➢ Prototype - nouvelle instance du service pour chaque injection
➢ Request - nouveau service par demande
➢ Session - nouveau service pour chaque session utilisateur
➢ Et bien d’autres…

171
Services – Injection

➢ Utilisation après injection


// UserController.groovy
class UserController {
➢ Injection par convention
// Injection par nom (minuscule)
➢ Idem pour def userService
➢ Services
➢ Bootstrap // Peut définir le type, équivalent
➢ Taglibs UserService userService

// UserService.groovy def index() {


class UserService { […]
def create(…) { userService.create("Bob", 25)
[…] […]
} }
} }
172
Grails – GORM & Hibernate

Queries

173
Grails – GORM – Querying – Basic CRUD

class Exemple {
➢ Créer
String name
Integer rating
➢ Créer une instance d'objet Boolean isValid
}
➢ Appeler la méthode save() pour demander à Hibernate de persister

// Création d’une nouvelle instance


def ExempleInstance = new Exemple(name: "name", rating: 10, isValid: true)

// Demande la persistance
ExempleInstance.save()

174
Grails – GORM – Querying – Basic CRUD

➢ save & delete


➢ flush option : demande une purge immédiate du contexte de persistance, persiste ou
supprime immédiatement
➢ Retourne « null » si la validation échoue

➢ save : options
➢ validate : détermine si l’étape de validation doit être ignorée
➢ insert : Lorsque définie à « true », force Hibernate à effectuer un INSERT SQL, utile dans
certaines situations (par exemple lors de l'utilisation d'identifiants assignés) et Hibernate
ne peut pas détecter s'il faut effectuer un INSERT ou un UPDATE.
➢ failOnError : si la valeur est "true", une exception sera levée et le serveur d'application
sera arrêté si la validation échoue. Doit être utilisé lors de la définition de données
essentielles.
➢ deepValidate : par défaut "true", si défini à false, ne valide pas les associations.

175
Grails – GORM – Querying – Basic CRUD

➢ Exemple

// On instancie un User
def userInstance =
new User(username: "username",
email: "user@email.com",
description: "description",
address: new Address())
.addToMessages(new Message(content: "message content"))

// Si la sauvegarde n’est pas réussie (échec de la validation)


if (!userInstance.save(flush: true))
println "Validation failed, User has not been saved to database"

176
Grails – GORM – Querying – Basic CRUD

➢ Lecture

➢ Choisissez soigneusement, impact fort sur les performances

// Récupération dans la base de données


def ExempleInstance = Exemple.get(1)

// Récupère une instance en « read-only »


def ExempleInstanceReadOnly = Exemple.read(1)

// Récupère un proxy au lieu de l’instance


def ExempleInstanceLoad = Exemple.load(1)

177
Grails – GORM – Querying – Basic CRUD

➢ Mise à jour
➢ Chargement d’une instance
➢ Modification des propriétés // Mise à jour
➢ Sauvegarde def ExempleInstance = Exemple.get(1)
ExempleInstance.name = "new name"
ExempleInstance.save()

// Supprimer
ExempleInstance.delete()
➢ Supprimer

178
Grails – Dynamic Finders

Chargement de données

179
Grails – GORM – Querying - Dynamic Finders

➢ Dynamic Finders
➢ Méthodes auto-générées à l’exécution
➢ Basé sur les propriétés des classes
➢ Utilisation comme n’importe quelle méthode statique
➢ Idéal pour des requêtes simples
➢ Rapidement difficile à lire / comprendre sur des requêtes longues
➢ Pas optimisé

➢ Where Queries
➢ Criteria Queries
➢ Hibernate Query Language (HQL)

180
Grails – GORM – Querying - Dynamic Finders

➢ Récupération de données
// Chargement via ID, exploite le cache, une requête
def userInstance = User.get(1)

// Strictement identique
// Plus lent
def sameUserInstance = User.findById(1)

// Récupération de liste
def allUsers = User.list()
def alsoAllUsers = User.getAll()

// Basé sur des propriétés


def user = User.findByUsername("username")
def userList = User.findAllByEmailLike("%@email.com")

181
Grails – GORM – Querying - Dynamic Finders

➢ Récupération de données

// A utiliser pour récupérer des données brut


def userList = User.getAll()

// Similaire via HQL, gère la pagination


def sameUserList = User.findAll("from User as u where u.username=?",
['username'],
[max: 10, offset: 5])

// Similaire à getAll, supporte la pagination et d’autres options


def anotherUserList = User.list(max: 10, offset: 5)

182
Grails – GORM – Querying - Dynamic Finders

➢ Dynamic Finders
class Book { class Author {
String title String name
Author author }
}

def bookInstance = Book.findByTitle("title")


assert bookInstance instanceof Book

def bookList = Book.findAllByTitle("title")


assert bookList instanceof List // même si une seule instance retournée

def authorInstance = Author.findByName("authorsName")


def otherBookInstance = Book.findByTitleLikeAndAuthor(“%title%", authorInstance)

183
Grails – GORM – Querying - Dynamic Finders

➢ Opérateurs
➢ InList
➢ Like / Ilike
➢ LessThan / LessThanEquals / GreaterThan / GreaterThanEquals
➢ IsNull / IsNotNull
➢ Between
➢ Rlike
➢ NotEqual
➢ InRange

184
Grails – GORM – Querying - Dynamic Finders

➢ Utiliser pour des cas simples

➢ Rapidement incompréhensible

185
Grails – Where Queries

Chargement de données

186
Grails – GORM – Querying – Where Queries

➢ Dynamic Finders
➢ Where Queries
➢ Plus flexible que les Dynamic Finders
➢ Moins verbeux que les Criteria
➢ Utilise les opérations de comparaison classiques
➢ En deux étapes
➢ Construction de la requête
➢ Exécution

➢ Criteria Queries
➢ Hibernate Query Language (HQL)

187
Grails – GORM – Querying – Where Queries

Opérateurs Description

== Egalité stricte

!= Différence

> Supérieur strict

< Inférieur strict

>= Supérieur ou égal

<= Inférieur ou égal

in Apparait dans la liste

==~ Like sensible à la casse

=~ Like insensible à la casse


188
Grails – GORM – Querying – Where Queries

➢ Exemples
class Book { class Author {
String title String name
Author author }
}

// Construction de la requête
def bookQuery = Book.where { title == "title" }

// Exécution de la requête
Book bookInstance = bookQuery.find()
List bookList = bookQuery.findAll()

// Alternative à l’execution immédiate


List otherBookList = Book.findAll { title == "title" }
List sortedBookList = Book.findAll (sort: "title") { title == "title" }
189
Grails – GORM – Querying – Where Queries

Méthode Description

second / minute / hour La seconde, minute ou heure d’une propriété


date
day / month / year Le jour, le mois ou l’année d’une propriété date

lower / upper Conversion de chaîne

length Longueur d’une chaîne

trim Trim une chaîne

// Récupère les commentaires de 2019


class Comment { def commentQuery = Comment.where {
Date dateCreated year(dateCreated) == 2019
} }
190
Grails – GORM – Querying – Where Queries

➢ Requêtes complexes

def bookQuery = Book.where {


// Sur le titre
(title ==~ "%title%" && title != "wrong title")
// Requête sur une association
author { name ==~ "%authorsName%" && name != "authorsName1" }
}
Book bookInstance = bookQuery.find()

191
Grails – GORM – Querying – Where Queries

Fonctions de sous-requêtes Description

avg La moyenne des valeurs

sum La sommes des valeurs

max Le maximum des valeurs

min Le minimum des valeurs

count SQL count

property Récupère une propriété de l’entité résultante

192
Grails – GORM – Querying – Where Queries

class Comment {
➢ Sous-requêtes Integer rating
➢ Impossible avec les Dynamic finders String comment
// Sélectionne les commentaires avec un rating String author
// supérieur à la moyenne
def commentQuery = Comment.where { static belongsTo = Book
rating > avg(rating) }
}

// Retourne les commentaires


// > avec un rating supérieur à la Moyenne des commentaires
// dont l’auteur est “Bob%”
// > sans être strictement “Bob”
def otherCommentQuery = Comment.where {
rating > avg(rating).of { author ==~ "Bob%" } && author != "Bob"
}

193
Grails – Criteria

Chargement de données

194
Grails – GORM – Querying – Criteria

➢ Dynamic Finders
➢ Where Queries
➢ Criteria Queries
➢ Plus flexible de que les « Where Queries »
➢ Bien plus verbeux
➢ Construction avec createCriteria() ou withCriteria()
➢ Conditions de niveau 0 liées par un « et » logique
➢ Possibilité de regrouper avec des blocks « and », « or » ou « not »
➢ Dernière possibilité avant de recourir au HQL

➢ Hibernate Query Language (HQL)

195
Grails – GORM – Querying – Criteria

Méthode Description

eq Egalité stricte

ne Différence

gt Supérieur à

lt Inférieur à

ge Supérieur ou égal à

le Inférieur ou égal à

inList Contenu dans la liste

like Like sensible à la casse

ilike Like non sensible à la casse

196
Grails – GORM – Querying – Criteria

➢ Exemple

// Critère sur le domaine “Comment”


def criteria = Comment.withCriteria {
// Vérifie que l’auteur est bien “Bob”
eq("author","Bob")
// Vérifie que la propriété “comment” contienne “comment”
ilike("comment","%comment%")
// Ou que le rating soit strictement supérieur à 3
or {
gt("rating", 3)
}
}

197
Grails – HQL

Querying

198
Grails – GORM – Querying – HQL

➢ Dynamic Finders
➢ Where Queries
➢ Criteria Queries
➢ Hibernate Query Language (HQL)
➢ Option la plus flexible
➢ Moyen le plus direct de s’adresser à l’ORM
➢ Moins d’abstraction, meilleures performances
➢ Construction avec find(), findAll() ou executeQuery()
➢ Mieux qu’utiliser du SQL pour conserver l’abstraction du SGBD

199
Grails – GORM – Querying – HQL

➢ Exemple

// Select HQL classique


def ExempleInstance = Exemple.find("from Exemple as e where e.name = 'name'")

// Avec paramètre nommés, toujours utiliser cette solution


def otherExempleInstance =
Exemple.find("from Exemple as e where e.name = :name",
[name: "name"])

200
Grails – GORM - Conclusion

201
Grails – Projet – Création du
Backend

Besoins, Configuration, Modèle, Associations, Contraintes, Scaffolding & Tests

202
Grails – Projet

➢ Backend : Besoin

➢ Configuration

➢ Définition des classes du modèle

➢ Associations

➢ Contraintes

➢ Scaffolding

➢ Tests
203
Grails – Projet – Besoins

Besoins & Configuration

204
Grails – Projet

➢ Backend
➢ User
➢ Annonce de vente
➢ Illustration

➢ Configuration
➢ Port de déploiement

205
Grails – Projet – Modèle

Construction des classes

206
Grails – Projet

➢ Définition des classes

➢ Classe User
➢ User name
➢ Creation date

➢ Classe Annonce
➢ Title
➢ Description
➢ Price
➢ Status
➢ Creation date
➢ Last update date

➢ Classe Illustration
➢ File name

207
Grails – Projet –
Associations

Création de relations

208
Grails – Projet

➢ Associations

➢ User – Annonce
➢ Un User peut publier plusieurs Annonces
➢ La collection d’Annonces n’a pas besoin d’être ordonnée
➢ Relation forte entre l’Annonce et l’User
➢ L’Annonce doit être supprimée si l’User est supprimé

➢ Annonce – Illustration
➢ Une Annonce peut avoir plusieurs Illustrations
➢ L’ordre peut être important dans ce cas
➢ Relation forte entre Annonce et Illustration
➢ Les Illustrations doivent être effacées si l’Annonce est effacée

209
Grails – Projet – Terminer et
tester

Contraintes, Scaffolding & Tests

210
Grails – Projet

➢ Contraintes

➢ Scaffolding

➢ Tests

211
Grails – Projet – Bootstrap

Définir les données d’initialisation

212
Grails – Conclusion

213
Grails – Conclusion

➢ Connaissances de bases sur le langage Groovy et le framework Grails


➢ Structure de projet
➢ Modélisation
➢ Associations
➢ Contraintes
➢ Mapping
➢ Contrôleurs
➢ Services
➢ GORM & Hibernate

➢ Backend basique fonctionnel


➢ Classes du domaine
➢ Associations
➢ Scaffolding

214
Module 3:
REST API
Concepts – Développement – Tests – Sécurité

215
Sommaire

➢ Vue d’ensemble – REST – SOAP


➢ Contraintes
➢ Principes d’implémentation
➢ Verbes HTTP
➢ Exemple d’échange
➢ Codes d’état HTTP
➢ Exemple avec Grails
➢ Sécurité

216
REST API
Concepts

217
REST

➢ Autour de 2000
➢ Roy Fielding PhD
➢ REpresentational State Transfer
➢ Repose intégralement sur HTTP
➢ On accède à des ressources
➢ Utilisation des verbes HTTP
➢ REST  RESTful

218
REST & SOAP

➢REST
➢ Principes architecturaux

➢ SOAP
➢ Spécifications d’un protocole de communication

219
REST & SOAP

➢ Architecture pour fournir des services web

➢ Différences majeures
➢ Implémentation
➢ Lisibilité
➢ WSDL
➢ Format du message
➢ Méthodes accessibles
➢ Emplacement

220
REST & SOAP – Comparaison ?

➢ REST ➢ SOAP
➢ Architecture ➢ Protocole
➢ Agnostique vis-à-vis du format ➢ seulement avec XML
➢ Repose intégralement sur HTTP ➢ Pas limité à HTTP
➢ Léger ➢ Fiable, Solide
➢ Lisible ➢ Moins lisible
➢ Gestion du cache (HTTP GET) ➢ HTTP POST, pas de Cache
➢ Une documentation doit être fournie ➢ WSDL
➢ Stateless ➢ Statefull ou Stateless
➢ Pas de surcharge des échanges ➢ Enveloppes SOAP + Headers optionnels
➢ Moins verbeux ➢ Structuré
➢ Pas de fonctionnalités optionnelles ➢ Couches optionnelles pour plus de
fonctionnalités

221
REST & SOAP

➢ Pas de réelle « meilleure solution »


➢ Deux solutions adaptées à des usages différents

➢ SOAP
➢ Besoin de sécurité
➢ Gestion de données de santé
➢ Domaine bancaire
➢ Besoin de fonctionnalités additionnelles

➢ REST
➢ Prototypes
➢ Besoin d’une grande segmentation Client - Serveur
➢ Besoin d’autre chose que du XML

222
REST API
Contraintes

223
REST – Propriétés & Contraintes

➢ Segmentation Client – Serveur


➢ Amélioration de la portabilité des interfaces utilisateurs
➢ Amélioration de la scalabilité
➢ Concentration sur le Serveur
➢ Composants indépendants
➢ Simplifie les évolutions

➢ Gestion du cache
➢ Clients et Serveurs peuvent mettre des données en cache
➢ Les Réponses doivent être explicitement déclarée comme « pouvant être mis en cache »
➢ Evite que les utilisateurs ne récupèrent des données périmées
➢ Améliore l'extensibilité

224
REST – Propriétés & Contraintes

➢ Stateless
➢ Le maintient de l’état est à la charge seule du Client
➢ Le Serveur ne stocke pas de contexte lié à ses Clients
➢ Accroit la segmentation Client – Serveur
➢ Les requêtes client doivent contenir toutes les informations nécessaires afin que le
Serveur puisse répondre de la manière appropriée
➢ Exception pour le stockage des sessions
➢ Afin que les User n’aient pas à renvoyer systématiquement les informations d’identification
➢ Pas de lien permanent entre le Client et le Serveur
➢ Pas de monopolisation de Serveur par un Client
➢ Pas de saturation Serveur même en cas d’accès concurrents
➢ Meilleure extensibilité

225
REST – Propriétés & Contraintes

➢ Système en couche
➢ Un système peut être segmenté en couches hiérarchiques
➢ Contraint le comportement des composants
➢ Chaque composant n’a de visibilité sur son n+1 ou n-1
Chaque composant est moins complexe
Réduction de la complexité globale
➢ Capacité à gérer des besoins croissants
➢ Load balancers
➢ Serveurs intermédiaires
➢ Doit être transparent du point de vue de l’utilisateur final
➢ Inconvénient : plus de couches : plus de latence
➢ Peut être limité avec l’utilisation de caches partagés

226
REST – Propriétés & Contraintes

➢ Code à la demande (Optionnel)


➢ Le Serveur peut temporairement étendre les fonctionnalités d’un client en lui servant du
code exécutable
➢ Java applets
➢ Javascript
➢ Accroit la flexibilité
➢ Réduit la visibilité
➢ Sécurité

227
REST – Propriétés & Contraintes

➢ Interface uniforme
➢ Identification des ressources
➢ Utilisation des URIs pour identifier les ressources
➢ Différencier la représentation d’une ressource de la ressource elle même
➢ Autorise une montée en version indépendante
➢ Manipulation des ressources au travers de leur représentation
➢ Une fois une ressource identifiée, manipulation facile via les différentes méthodes HTTP (GET, POST, PUT &
DELETE)
➢ Message auto-descriptifs
➢ Chaque message doit inclure assez d’information pour être capable de le comprendre et donc de pouvoir le
gérer correctement (formats, cache, timestamps)
➢ Hypermedia as the engine of application state (HATEOAS)
➢ Les réponses peuvent inclure des liens, donnant la possibilité au client de naviguer au travers des différentes
actions possible pour la ressource

➢ Pour et contre ?
➢ Les données sont normalisées, la structure et prédictible
➢ L’évolution des composants est plus simple
➢ L’implémentation côté client peut être plus difficile
➢ Réduit les performances globales

228
REST API
Principe d’implémentation

229
REST – Principe d'implémentation

➢ Définition des ressources et collections de ressources


➢ Création des ressources
➢ Attributs
➢ Contraintes
➢ Définition des formats d’échange
➢ Format unique
➢ Multi format

230
REST – Principe d'implémentation

➢ Sémantique du message liée à la méthode HTTP

➢ Verbe HTTP / Méthode RFC2616


➢ GET : Récupère une ressource / collection de ressource
➢ POST : Création de ressources
➢ PUT / PATCH : Mise à jour d’une ressource existante
➢ DELETE : Suppression d’une ressource
➢ HEAD : Récupération de metainformation sur la ressource (~GET)
➢ Pas de corps dans la réponse
➢ Métadata uniquement
➢OPTIONS : Détermine les actions possible sur une ressource sans initier de récupération de
ressource
➢ Partie sous estimée du protocol HTTP
➢ Peut être utilisé pour améliorer l’interconnection de services

231
REST – Format handling

➢ Le Client demande un format via le header « Accept »

Accept: <MIME_type>/<MIME_subtype>
Accept: <MIME_type>/*
Accept: */*

➢ Le Serveur confirme le format avec le header « Content-type »

Content-Type : <MIME_type>/<MIME_subtype>
Content-Type : <MIME_type>/*
Content-Type : */*

232
REST API
Méthodes HTTP

233
REST – Méthodes HTTP – GET

➢ Utilisé pour la récupération de représentation de ressource


➢ Pas de corps de requête
➢ Paramètres après l’URL
➢ Mise en cache
➢ Mise en favoris
➢ Ne jamais créer / update / supprimer une ressource
➢ Idempotente
➢ Return
➢ HTTP 200 (OK)
➢ HTTP 404 (NOT FOUND)
➢ HTTP 400 (BAD REQUEST)

234
REST – Méthodes HTTP – POST

➢ Utilisé pour la création de nouvelle ressource


➢ Pas de mise en cache
➢ Pas de mise en favoris
➢ Alternative à GET quand trop de paramètres
➢ Return
➢ HTTP 201 (CREATED)
➢ HTTP 204 (NO CONTENT)
➢ HTTP 404 (NOT FOUND)
➢ HTTP 400 (BAD REQUEST)

235
REST – Méthodes HTTP – PUT

➢ Utilisé pour la mise à jour de ressource


➢ Pas de mise en cache
➢ Pas de mise en favoris
➢ Certaines API créeront une ressource si elle n’existe pas encore
➢ Idempotente
➢ Return
➢ HTTP 200 (OK)
➢ HTTP 201 (CREATED)
➢ HTTP 204 (NO CONTENT)
➢ HTTP 404 (NOT FOUND)
➢ HTTP 400 (BAD REQUEST)

236
REST – Méthodes HTTP – PATCH

➢ Utilisé pour la mise à jour de ressource


➢ Pas de mise en cache
➢ Pas de mise en favoris
➢ Devrait être préféré à PUT dans le cas des mises à jour partielles
➢ Return
➢ HTTP 200 (OK)
➢ HTTP 204 (NO CONTENT)
➢ HTTP 404 (NOT FOUND)
➢ HTTP 400 (BAD REQUEST)

237
REST – Méthodes HTTP – DELETE

➢ Utilisé pour la suppression de ressource


➢ Pas de mise en cache
➢ Pas de mise en favoris

➢ Return
➢ HTTP 200 (OK)
➢ HTTP 204 (NO CONTENT)
➢ HTTP 404 (NOT FOUND)
➢ HTTP 400 (BAD REQUEST)

238
REST – Méthodes HTTP – OPTIONS

➢ Utilisé pour récupérer les options de communication disponible


➢ Pas de mise en cache
➢ Pas de mise en favoris
➢ Ne déclenche pas d’action sur les ressources
➢ Return
➢ HTTP 200 (OK)
➢ HTTP 404 (NOT FOUND)

HTTP/1.1 200 OK
Allow: GET,HEAD,POST,OPTIONS
Content-Type: text/html; charset=UTF-8
Date: Wed, 25 Sep 2019 13:17:54 GMT
Content-Length: 0

239
REST – Méthodes HTTP – HEAD

➢ Utilisé pour récupérer des métainformations


➢ Mise en cache
➢ Pas de mise en favoris
➢ Ne doit retourner aucun corps de réponse
➢ Même réponse qu’un GET sans corps
➢ Return
➢ HTTP 200 (OK)
➢ HTTP 404 (NOT FOUND)

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Date: Wed, 25 Sep 2019 13:17:54 GMT
Content-Length: 15426
Last modified: Tue, 24 Sep 2019 00:00:00 GMT
240
REST API
Exemple d’échange

241
REST – Echange (1)

➢ Côté client – Construction de requête

➢ Définition de ressource
➢ Ressource singleton
➢ Collection de ressource

➢ Définition de l’action, utilisation de la méthode HTTP appropriée


➢ GET
➢ POST
➢ PUT
➢ PATCH
➢ DELETE
➢ HEAD
➢ OPTIONS

242
REST – Echange (2)

➢ Côté Serveur – Gestion de la requête et résolution

➢ Définition de la ressource demandée (URI)

➢ Définition de la méthode HTTP utilisée

➢ (Optionnel) Contrôle d’accès, permissions


➢ Lire et vérifier un token

➢ Résoudre l’action

➢ Génération de la réponse
➢ Définition du format
➢ Retourner la représentation de la ressource ou de la collection de ressources dans le format approprié
➢ Retourne le code HTTP approprié

243
REST – Echange (3)

➢ Côté Client – Lecture de la réponse


➢ Vérifier le code HTTP retourné
➢ HTTP 2xx : OK
➢ HTTP 3xx : redirection
➢ HTTP 4xx / 5xx : KO
➢ HTTP 2xx, dans le cas d’une requête…
➢ GET : Parser la réponse pour récupérer les données
➢ POST : La ressource a été correctement créée
➢ PUT / PATCH : La ressource a été correctement mise à jour
➢ DELETE : La ressource a été correctement supprimée
➢ HTTP 4xx / 5xx
➢ Une erreur s’est produite
➢ Possibilité de trouver une explication dans la réponse
➢ Le code HTTP peut donner une information précise sur la raison

244
REST – Echange – Conclusion

➢ Similaire au échanges HTTP classiques

➢ Construction de requête très basique


➢ Dépend de l’environnement
➢ Utilisation des librairies classiques dans les environnements mobile
➢ Utilisation d’Ajax dans les environnements web

➢ Gestion des réponses


➢ XML / JSON / … parser

245
REST – Echange – Conclusion

➢ Tests
➢ Utilisation des outils de développement / débug des navigateurs
➢ Chrome DevTools
➢ Firefox Firebug
➢ cURL
➢ Postman

➢ Connaissance approfondie de HTTP est un plus : RFC2616

➢ List des codes HTTP : List

➢ Connaissance de base des mécanismes de redirection

246
REST API
Codes de réponse HTTP

247
REST – Codes de réponse HTTP

➢ 200 OK
➢ Requête traitée avec succès
➢ Contenu dépend de la méthode HTTP utilisée

➢ 201 CREATED
➢ Requête traitée avec succès
➢ La ressource a été créée

➢ 204 NO CONTENT
➢ Requête traitée avec succès
➢ Pas de donnée retournée

248
REST – Codes de réponse HTTP

➢ 400 BAD REQUEST


➢ Le Serveur ne traitera pas la requête à cause d’une erreur
➢ Requête malformée
➢ Erreur sur les paramètres fournis
➢ …

➢ 401 UNAUTHORIZED
➢ Le Serveur n’autorisera pas l’accès à la ressource demandée sans authentification

➢ 403 FORBIDDEN
➢ La requête est valide mais l’authentification fournie ne permet pas d’accéder à la
ressource demandée

➢ 404 NOT FOUND


➢ La ressource demandée est introuvable

249
REST – Codes de réponse HTTP

➢ 405 METHOD NOT ALLOWED


➢ La méthode HTTP utilisée n’est pas utilisable sur la ressource

➢ 500 INTERNAL SERVER ERROR


➢ Message d’erreur générique
➢ Souvent retourné directement par les serveurs d’application
➢ Ne jamais retourner cette erreur explicitement, trop vague

➢ 301 MOVED PERMANENTLY


➢ Doit fournir une nouvelle URL pour la ressource ciblée
➢ Utiliser ceci pour les redirection avec et sans slash final

➢ 302 FOUND – MOVED TEMPORARILY


➢ Doit fournir une nouvelle URL pour la ressource ciblée

250
REST API – HATEOAS & HAL
Hypermedia As The Engine Of Application State

251
REST – HATEOAS – Concept

➢ Hypermedia As The Engine Of Application State


➢ Contrainte de REST
➢ Le client n'a pas besoin de connaître l'application déployée
➢ Augmenter la segmentation Client - Serveur

252
REST – HATEOAS – Concept

➢ Réponse classique d'une API REST

253
REST – HATEOAS – Concept

➢ Réponse d'une API REST mettant en œuvre la contrainte HATEOAS

254
REST – HATEOAS – HAL

➢ HAL : Hypertext Application Language

➢ Une des nombreuses implémentations de HATEOAS

➢ Principes
➢ Format simple pour lier les ressources
➢ Rendre l'API navigable

➢ Plus difficile à mettre en place au début

➢ Nombreuses bibliothèques pour produire et consommer HAL

➢ Définir des conventions pour décrire les ressources en JSON ou XML.

255
REST – HATEOAS – HAL

➢ Objectifs
➢ Consacrer moins de temps à la conception du format
➢ Se concentrer sur l'implémentation et la documentation
➢ Construire des APIs « explorables »
➢ Les associations permettent d'identifier les ressources et les interactions possibles

➢ Point d'entrée de base permettant au développeur de découvrir les liens

➢ Peut être considéré comme un descripteur d'API

➢ L'exploration des API rend la documentation « presque inutile »

256
REST – HATEOAS – HAL

➢ Basé sur deux types de représentations

➢ Ressources
➢ Contiennent des liens
➢ Définissent d'autres sous-ressources
➢ Définissent un état

➢ Lien
➢ Définit une cible (URI)
➢ Lié à une relation (rel)
➢ Contient des Propriétés optionnelles pour gérer la gestion des versions et des formats.

257
REST – HATEOAS – HAL

258
REST – HATEOAS – HAL – Example

➢ Liens vers des ressources (1)

➢ Propriétés des ressources (2)

➢ Sous-ressources et leurs liens (3)

259
REST – HATEOAS – CURIEs

➢ URIs compacts
➢ Raccourci pour les liens
➢ Peut avoir de nombreuses variantes
➢ Utilisation d'un caractère de remplacement ({rel})
➢ Appel de la méthode find sur http://example.com/docs/rels/orders{?id}

260
REST – HATEOAS – HAL – Content Type

➢ REST classique

➢ Spécifique à HAL

261
REST API – Exemple détaillé
Mise en œuvre et test d’une API REST

262
REST – Implémentation

➢ Modèle basique avec deux entités

class Book {
class Library {
String title
String name
String author
String address
Date dateCreated
static hasMany = [books: Book]
static belongsTo = [library: Library]
}
}

263
REST – Implémentation

➢ Données d’initialisation

class BootStrap {
def init = { servletContext ->
new Library(name: "Library name", address: "Library address")
.addToBooks(new Book(title: "Library's first book", author: "Unknown"))
.addToBooks(new Book(title: "Library's second book", author: "Unknown"))
.addToBooks(new Book(title: "Library's last book", author: "Unknown"))
.save(flush:true, failOnError: true)
}
}

264
REST – Implémentation – Resources

➢ Ressources disponibles

➢ Library
➢ Bibliothèque
http://my.server.com/library/{id}
➢ Collection de bibliothèques
http://my.server.com/libraries

➢ Book
➢ Livre
http://my.server.com/book/{id}
➢ Collection de livres
http://my.server.com/books

265
REST – Implémentation – Actions disponibles

URI Description Responses


GET http://server/library/ Récupère la liste des 200 OK + Représentation de la collection de
GET http://server/libraries/ bibliothèques ressources / 404 / …
POST Crée une bibliothèque 201 OK
http://server/library/ 404 / …
http://server/libraries/
GET Récupère une 200 OK + Représentation de la ressource
http://server/library/id bibliothèque via son id 404 / …
PUT Mise à jour de la 200 OK
http://server/library/id bibliothèque 404 / …
DELETE Suppression de la 200 OK
http://server/library/id bibliothèque 404 / …

266
REST – Implémentation – Actions disponibles

URI Description Réponses


GET http://server/library/ Récupère la liste des 200 OK + Récupération de la représentation de la
GET http://server/libraries/ bibliothèques collection de ressources / 404 / …

➢ Deux façons d’accéder à une collection (GET / POST)

➢ http://server/library
➢ Considéré par certains comme faux ou pas assez précis

➢ http://server/libraries
➢ Considéré comme étant la meilleure option la plus part du temps

➢ Certains utilisent les deux avec des redirections

267
REST – Implémentation – Actions disponibles

URI Description Réponses


POST Crée une bibliothèque 201 OK
http://server/library/ 404 / …
http://server/libraries/
PUT Mise à jour de la 200 OK
http://server/library/id bibliothèque 404 / …

➢ Il est important de différencier la ressource de la collection de ressources

➢ Les requêtes GET et POST ciblent la collection de ressources.

➢ Les requêtes GET / PUT / PATCH / DELETE ciblent une simple ressource.
➢ Identifiée par un ID

268
REST – Implémentation – Actions disponibles

GET http://server/book/1

GET http://server/books/
269
REST – Implémentation – Tests

➢ Client Url Request Library


➢ Construire et exécuter des requêtes HTTP
➢ Récupérer la réponse du serveur

➢ Options importantes
➢ -X Méthode HTTP
➢ -I Affiche le header de la réponse
➢ -H Définition du header de la requête
➢ -d Définition des données à transmettre

➢ -v Verbeux

270
REST API – Grails
Implémentation
Implémentation d’une API REST basique & tests

271
REST – Implémentation – Grails

➢ Étapes pour l'API REST la plus simple


➢ Créer un contrôleur pour gérer les demandes du client « ApiController »
➢ Définir des méthodes pour gérer les différents points d'entrée.

class Book {
class Library {
String title
String name
String author
String address
Date dateCreated
static hasMany = [books: Book]
static belongsTo = [library: Library]
}
}

272
REST – Implémentation – Grails

// ApiController.groovy
class ApiController {

/**
* Gèrera les requêtes GET / PUT / PATCH / DELETE
*/
def book() {render "ok"}
/**
* Gèrera les requêtes GET / POST
*/
def books() {}

def libraries() {}

def library() {}
}
273
REST – Implémentation – Grails

➢ Test avec cURL

$ curl http://localhost:8080/api/book -I
HTTP/1.1 200
Content-Type: text/html;charset=utf-8
Transfer-Encoding: chunked
Date: Wed, 02 Oct 2019 17:33:29 GMT

274
def book() {
switch(request.getMethod())
{
REST – Implémentation case "GET":
if (!params.id)
return response.status = 400
def bookInstance = Book.get(params.id)
➢ Implémentation simple if (!bookInstance)
➢ Gérer les erreurs de base return response.status = 404
➢ Gérer les méthodes de requête response.withFormat {
➢ Gérer les formats de base xml { render bookInstance as XML}
json { render bookInstance as JSON }
}
➢ Dans tous les cas break
➢ Retourner un code HTTP explicite case "PUT":
break
case "PATCH":
break
case "DELETE":
break
default:
return response.status = 405
break
}
return response.status = 406
275

}
REST – Implémentation – Grails

➢ Test avec cURL


$ curl http://localhost:8080/api/book/1 -H "Accept: application/json" -X GET -i
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 02 Oct 2019 17:47:49 GMT

➢ Donnée retournée

276
REST – Implémentation – Grails

➢ On teste aussi les cas d’erreur : Content-type non géré


$ curl http://localhost:8080/api/book/1 -H "Accept: text/csv" -X GET -i
HTTP/1.1 406
Content-Length: 0
Date: Wed, 02 Oct 2019 17:47:49 GMT

➢ Autre cas : Méthode HTTP non disponible


$ curl http://localhost:8080/api/book/1 -H "Accept: text/json“ -X POST -i
HTTP/1.1 405
Content-Length: 0
Date: Wed, 02 Oct 2019 17:47:49 GMT

277
REST – Implémentation – Grails

➢ Gestion du format
➢ Plusieurs possibilités

response.withFormat {
switch(request.getHeader("Accept"))
xml { render bookInstance as XML}
{
json { render bookInstance as JSON }
case 'json':
}
case 'text/json':
case 'application/json':
➢ Logique basique render bookInstance as JSON
break
case 'xml':
case 'text/xml':
case 'application/xml':
render bookInstance as XML
break
}
278
REST – Implémentation – Grails

➢ Génération d'API avec des annotations

➢ API REST complète


@Resource(uri = 'books', formats=['json', 'xml'])
➢ Tous les formats sont gérés class Book {

String title
String author
Date dateCreated

static belongsTo = [library: Library]

static Contraintes = {
}
}
279
REST – Implémentation – Grails

➢ Possibilité de mapper des ressources REST à partir du fichier URLMapping

class UrlMappings {
static mappings = {
"/books"(resources: "book", excludes:['update','create'])
"/books"(resources: "book", includes:['index','show','delete'])
"/books"(resources: "book")
{
"/authors"(resources: "author")
}
}
}

280
REST API – Sécurité
Mécanismes de protection

281
REST – Sécurité

➢ L'un des principaux problèmes des API REST

➢ Nombreuses solutions
➢ Authentification
➢ Token
➢ Signature
➢ Demandes signées à usage unique
➢ HTTPS

282
REST – Sécurité – Hash

➢ Empreinte d'un fichier / chaîne de caractères / ...


➢ Taille fixe
➢ A l'origine pour les contrôles d'intégrité
➢ Faiblesses : inversion du hachage
➢ Dictionnaires – Brut force
➢ Liste de chaînes et de hachages correspondants
➢ 500 mots de passe les plus utilisés = 75% des utilisateurs
➢ Quelques secondes si moins de 6 caractères (lettres, chiffres et caractères spéciaux compris)
➢ Rainbow tables
➢ Même principe mais précalculé
➢ Échange du traitement contre le stockage
➢ Faible contre le hachage salé (ne peut pas précalculer tous les mots de passe possibles avec tous les sels
possibles)

283
REST – Sécurité – Authentification

➢ Répond au besoin d'identifier l'expéditeur de la requête

➢ Authentification classique
➢ Envoyer le login + le mot de passe (hash) dans la requête
➢ Toujours utiliser un "sel" pour améliorer la sécurité

➢ Problèmes
➢ Peut facilement faire l'objet d'une force brute
➢ Informations d'identification envoyées
➢ Si la requête est interceptée, nous pouvons utiliser l'API avec les identifiants récupérés

284
REST – Sécurité – Token

➢ Implémentation simple pour limiter les échanges contenant des identifiants


➢ Les informations d'identification sont envoyées pour récupérer un token avec une durée de
vie limitée
➢ On utilise alors le "Token" pour jouer le rôle des informations d'identification
➢ Le jeton n'a pas de lien évident avec les informations d'identification
➢ Nous limitons les risques

➢ Problème
➢ Si le « Token » est intercepté, nous pouvons toujours utiliser l'API jusqu'à la fin de la durée
de vie du « Token »

285
REST – Sécurité – Signature

➢ Vise à garantir l'identité de l'expéditeur ainsi que l'intégrité de la requête


➢ Utilisation d'algorithmes de type HMAC
➢ Calculé via un "Hash" en combinaison avec une clé secrète
➢ Expéditeur
➢ Chiffre le document et le hash du document avec sa clé privée
➢ Récepteur
➢ Identifie l'expéditeur en décryptant l'ensemble pour obtenir le hash (qui ne peut être créé que par le
porteur de la clé privée).
➢ Vérifier l'intégrité du document en reconstruisant le hachage du document et en le comparant.

➢ En cas d'interception, il est impossible de recréer une signature valide pour une
nouvelle demande.

➢ Problème
➢ Si la demande est interceptée, elle peut être rejouée.

286
REST – Sécurité – Signature

➢ Encryption / Decryption : Alice souhaite envoyer un document à Bob. Elle


dispose de la clef publique de Bob. Bob a également cette clef, ainsi que la clef
privée qui lui correspond (et qu’il est le seul à avoir)

287
REST – Sécurité – Signature

➢ Signature / Vérification : Bob souhaite envoyer un document à Alice. Alice


pourra le vérifier et garantir qu’il vient bien de Bob

288
REST – Sécurité – Requête signée à usage
unique

➢ Solution équivalente utilisée par la majorité des fournisseurs d'API REST


sérieux.

➢ Stockage supplémentaire de l'horodatage de la dernière requête par le client et


le serveur.

➢ Utilisation de cet horodatage pour la création de la signature.

➢ Résout le dernier problème restant : même si la requête est interceptée, elle ne


peut pas être rejouée.

289
REST – Sécurité – HTTPS

➢ Sécurité du canal

➢ Assurance pour le serveur que la requête reçue est identique à celle envoyée.

➢ Assurance que l'expéditeur est bien la personne qui prétend avoir envoyé la
demande.

➢ Cryptage de bout en bout

➢ Man in the middle inutile

➢ Combiné à d'autres méthodes pour plus de sécurité


290
Conclusion
Et maintenant ?

291
REST API – Projet
Construction d’une API REST basique

292
REST API – Projet

➢ Contrôleur

➢ Construire une API basique

➢ Tests

➢ Etapes suivantes

➢ Amélioration de l’API REST

➢ Mise en place d’une sécurité pour cette API

293
Module 4 :
Spring Security

Framework Grails & Sécurité

294
Authentification

➢ Pas de gestion native

➢ Possibilité d’utiliser des intercepteurs pour une sécurité basique

➢ Utilisation de plugins pour une meilleure sécurité de base

➢ Spring Security

➢ Shiro

➢ Focus sur Spring Security

295
Spring Security

➢ Crée et gère des classes

➢ User

➢ Role

➢ UserRole

➢ Initialisation après l’installation

➢ Script à lancer

296
Spring Security

➢ s2- quickstart

➢ Exemples

➢ grails s2-quickstart com.mooc.tpgrails User Role UserRole

➢ grails s2-quickstart com.mooc.tpgrails Person Authority

➢ grails s2-quickstart com.mooc.tpgrails Person Authority Permissions

297
Spring Security

➢ Todo après l’installation

➢ Vérifier les classes créées

➢ Personnaliser

➢ Configurer

➢ Très flexible

➢ Très bien documenté

298
Spring Security : TagLibs

➢ Security TagLibs

<sec:ifLoggedIn>
<g:link controller="">Special link only accessible when logged in</g:link>
</sec:ifLoggedIn>

<sec:ifNotLoggedIn>
<g:link controller="">Special link only accessible when not logged in</g:link>
</sec:ifNotLoggedIn>

<sec:ifAnyGranted roles="ROLE_ADMIN, ROLE_MODERATOR">


<g:link controller="">Special link only accessible when logged in with specific role</g:link>
</sec:ifAnyGranted>

299
Spring Security : Service

➢ Nouveau Service disponible : SpringSecurityService

➢ Plein de méthodes utiles

➢ getCurrentUser()
➢ Retourne l’instance de l’utilisateur identifié

➢ isLoggedIn()
➢ Retourne un booléen indiquant si l’utilisateur courant est identifié

➢ getAuthentication()
➢ Retourne l’utilisateur identifié dans un objet

300
Spring Security : Events

➢ Spring Security déclenche des évènements après certaines actions liées à la


sécurité

➢ Login validé

➢ Login non validé

➢…

➢ Très utile dans des cas spécifiques

➢ Lancer des routines de vérification

➢ Edition d’un élément lorsqu’un utilisateur s’est identifié

301
Spring Security : Domain

➢ Changement des classes du domain

➢ Besoin de changer quelques petites choses dans le Bootstrap pour gérer la nouvelle
structure

➢ Etapes

➢ Créer / récupérer un Rôle

➢ Créer / récupérer un Utilisateur

➢ Créer une relation entre les précédents

302
Spring Security : Bootstrap example

➢class BootStrap {
def init = {
def adminRole = new Role(authority: 'ROLE_ADMIN').save()
def userInstance = new User(username: 'username', password: 'password').save()
UserRole.create userInstance, adminRole
UserRole.withSession {
it.flush()
it.clear()
}
assert User.count() == 1
assert Role.count() == 1
assert UserRole.count() == 1
}
}

303
Spring Security

➢ Logout uniquement en POST dans la configuration par défaut

# config/application.groovy
grails.plugin.springsecurity.logout.postOnly = false

➢ Méthode de hachage du mot de passe

➢ Bcrypt : defaut

➢ Sha-256

➢ PBKDF2

304
Spring Security

➢ Annotations

@Secured(value=["hasRole('ROLE_ADMIN')"], httpMethod='POST')
def index(Integer max) {

➢ S’applique aux

➢ Méthodes

➢ Contrôleurs

➢ Définition du type de sécurité dans la configuration

grails.plugin.springsecurity.securityConfigType = "Annotation"

305
Spring Security

➢ Static Rules

➢ Compatible avec les Annotations

grails.plugin.springsecurity.controllerAnnotations.staticRules = [
[pattern: '/', access: ['permitAll']],
[pattern: '/**/css/**', access: ['permitAll']],
[pattern: '/**/images/**', access: ['permitAll']],
[pattern: '/**/favicon.ico', access: ['permitAll']]
]

➢ Peut cibler un chemin même s’il n’est pas lié à un contrôleur / méthode

306
Spring Security

➢ Intercept Url Map

➢ Plus flexible

➢ Première règle valide retenue

➢ RequestMap

➢ Stocké en base de donnée

➢ Utile pour les changements à chaud

➢ Plus précis (se comporte comme les sélecteurs CSS)

307
Spring Security

Live Coding : Mise en place

308
Objectifs

➢ On reprend le projet

https://gitlab.com/mooc-sources/grails-salead

➢ Reprenez la bonne branche : basic-crud-rest-api

➢ C’est partis

309
Objectifs

➢ Ajout et configuration du plugin

➢ Spring Security

➢ Lancement du script d’installation

➢ Adaptation de la classe « User »

➢ Vérification de la configuration

➢ Modification du Bootstrap

➢ Test

310
Objectifs

➢ Ajouter et configurer Spring Security REST

➢ Définir le JWT

➢ Mise à jour de la filter chain

➢ Ajout des Annotations sur les ressources

➢ Retirer les anciens points d’entrée pour utiliser l’API générée

➢ Changer les mappings

➢ Vérifier l’authentification

➢ Tester l’API

311
Conclusion

Spring Security

312
Ressources

➢ Documentation de Spring Security


https://grails-plugins.github.io/grails-spring-security-core/3.2.x/

➢ Documentation de Spring Security REST


https://alvarosanchez.github.io/grails-spring-security-
rest/2.0.0.RC1/docs/index.html

➢ JWT à utiliser
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJtb29jX3NwcmluZ19zZWN1cml
0eSIsImlhdCI6MTU5NjIwNjcxMywiZXhwIjoxNjI3NzQyNzEzLCJhdWQiOiJtb29jX3N0d
WRlbnRzIiwic3ViIjoibW9vY19zcHJpbmdfc2VjdXJpdHkiLCJHaXZlbk5hbWUiOiJNb29jI
n0.gx1iCqhrx1gikFigcUTqlBBdGZPbXs6bZYxDp5V93fs

313
Documentations

➢ Groovy
http://docs.groovy-lang.org/next/html/documentation/
➢ Grails Core
https://docs.grails.org/latest/guide/single.html
➢ GORM for Hibernate
http://gorm.grails.org/latest/hibernate/manual/
➢ Spring SecurityCore
https://grails-plugins.github.io/grails-spring-Sécurité-core/latest/
➢ Hibernate
https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_Us
er_Guide.html
➢ Asset Pipeline
http://www.asset-pipeline.com/manual/index.html
314
Références – Liens

➢ Grails plugins portal


http://plugins.grails.org/

315
Références – Illustrations

➢ Grails plugins portal


http://plugins.grails.org/
➢ Illustration de l’architecture Grails
http://www-igm.univ-
mlv.fr/~dr/XPOSE2009/Groovy_and_Grails/grails_architecture.php
➢ Illustration types de données et équivalents en base
https://grailsinaction.com/tag/data-type/
➢ Illustration sur les limitations de chaque type de requête
http://tatiyants.com/how-and-when-to-use-various-gorm-querying-options/
➢ Certains codes sources, inspirations pour certains exemples
http://docs.grails.org/latest/
http://groovy-lang.org/
http://grails.asia/
http://www.mrhaki.com/

316
Références – Illustrations

➢ Illustration static vs dynamic typing


https://twitter.com/01k/status/1067788059989684224
➢ Contraintes list
https://docs.grails.org/snapshot/ref/Contraintes/Usage.html

317

Vous aimerez peut-être aussi