diff --git a/doc/client.md b/doc/client.md
index 8a947f52e34549200d5744106ae559633f6a53a7..31780c407861c3ac71a1642693b10dd8409080db 100644
--- a/doc/client.md
+++ b/doc/client.md
@@ -82,6 +82,6 @@ unicaenVue.axios.get(
 });
 ```
 
-Lorsqu'il reçoit une réponse du serveur, si cette dernière comporte des messages issus du flashMessenger, il va les afficher sous formes d'alertes.
-Les alertes sont personnalisées selon qu'il s'agit de success, warning, info ou error. Elles sont affichées pendant 5 secondes, sauf les erreurs qu'il faut fermer 
+Lorsqu'il reçoit une réponse du serveur, si cette dernière comporte des messages issus du flashMessenger, il va les afficher sous formes de toasts.
+Les toasts sont personnalisées selon qu'il s'agit de success, warning, info ou error. Ils sont affichés pendant 3 secondes, sauf les erreurs qu'il faut fermer 
 manuellement afin d'avoir le temps de lire le message et de le traiter.
diff --git a/doc/serveur.md b/doc/serveur.md
index 6fa0a5728921666366eaf11af47679e8d6993f67..485b3cb5c308e356b463cfa2324503ce253091df 100644
--- a/doc/serveur.md
+++ b/doc/serveur.md
@@ -112,6 +112,11 @@ Ce dernier va :
 - collecter les éventuels messages issus du `flashMessenger`
 - convertir le tout en Json pour envoi au client
 
+AxiosModel peut prendre en construction kjusqu'à 3 paramètres :
+1. Les données en tant que telles
+2. Les prioriétés (utiles seulement si on traite des objets et facultatives si on transmet des objets implémentant AxiosInterface)
+3. Les triggers (si nécessaire). Vous trouverez [plus d'infos sur les triggers ici](#les-triggers).
+
 Voici un exemple d'action de contrôleur qui envoie des données au client en mode Ajax vers Axios :
 
 ```php
@@ -133,6 +138,7 @@ use UnicaenVue\View\Model\AxiosModel;
         $this->flashMessenger()->addSuccessMessage('Tout va bien!');
 
         // Et on retourne un AxiosModel qui présente le tout au client
+        // Ici, nous traitons un tableau en entrée, donc pas besoin de préciser des propriétés à extraire et nous n'avons pas besoin de triggers
         return new AxiosModel($contacts[$monId]);
         
         // Si on exporte un objet ou un tableau d'objets, on peut aussi fournir une lisye de propriétés, cf. ExiosExtractor
@@ -361,6 +367,85 @@ var_dump($extracted);
 //      'ville' => string 'Caen' (length=4)
 ```
 
+#### Les triggers
+
+Il est possible de transformer les données traitées via Axios au moyen de triggers.
+Ce sera utile si, par exemple, on veut ajouter des propriétés à la volée à un objet qui n'en était pas pourvu ou si on veut modifier 
+après coup la valeur de certaines données.
+
+Il s'agit d'un usage avancé du dispositif.
+
+Les triggers sont un tableau de fonctions dont les clés sont les chemins qui vont permettre de déterminer sur quelles données nous
+voudrons agir, et les valeurs sont des fonctions anonymes dotées de deux paramètres $original : les données originales, c'est-à-dire avant extractions et $extracted, c'est-à-dire le résultat extrait de l'original.
+La fonction anonyme devra renvoyer un résultat qui remplacera $extracted dans l'arborescence finale.
+
+Les chemins sont définis de la manière suivante :
+ * Si on a une liste d'objets, alors `'/'` permettra d'atteindre chaque occurence du tableau renvoyé.
+ * Si on y ajoute `[]`, ce qui donnera `'/[]'`, alors on pourra agir sur tout le tableau de données (à condition toutefois que ce soit bien une liste de données qu'on manipule).
+ * Si on veut accéder à des données plus en avant dans l'arborescence, alors on la parcourera avec des slashs `/``, comme des répertoires.
+
+Exemple de chemin plus complexe : prenons la structure de données citée [ci-dessous](#depuis-doctrine).
+Si nous voulons agir sur les créateurs des volumes horaires, il faudra utiliser le chemin suivant :
+`/volumesHoraires/histoCreateur`.
+Nous pourrons ainsi traiter les créateurs des volumes horaires, et pas les créateurs des missions.
+
+
+
+Exemple d'utilisation des triggers :
+
+Prenons la structure de données ci-dessus, à savoir une liste de personnes ayant une adresse.
+
+Voici un exemple de trigger qui va ajouter l'âge aux personnes :
+
+```php
+$personnes = [new Personne(), new Personne()];
+
+$properties = ['nom', 'prenom', 'isMajeur', 'adresse'];
+
+$triggers = [
+    // '/' signifie que nous agirons sur les données de premier niveau, qui sont ici des Personne. Le trigger agira pour chaque personne
+    '/' => function($original, $extracted){
+        // $original contiendra l'objet correspondant à l'entité Personne
+        // $extracted contiendra le tableau de données déjà extrait
+        
+        $extracted['age'] = 37; // On ajoute ici une propriété en extraction qui n'a pas été générée avant.
+        // Nous pourrions tout aussi bien retirer une donnée, ou bien en changer le type ou la valeur.
+        
+        return $extracted;
+    },
+    
+    // Autre exemple de trigger qui agira non plus sur chaque personne de la liste, mais sur l'ensemble de la liste
+    '/[]' => function($original, $extracted){
+        // $original contient la liste des objets Personne
+        // $extracted contient un tableau des deux personnes converties en array
+        
+        unset($extracted[0]);// on supprime la première personne de la liste
+        
+        // il est obligatoire de retourner quelque chose
+        return $extracted;
+    },
+    
+    // Encore un autre exemple : on va agir sur les adresses de chaques personnes
+    '/adresse' => function($original, $extracted){
+        $extracted['pays'] = 'France';
+        
+        return $extracted;
+    }
+];
+
+$extracted = \UnicaenVue\Axios\AxiosExtractor::extract($personnes,$properties,$triggers);
+var_dump($extracted);
+// Renvoie :
+// array (size=4)
+//  'nom' => string 'Dupont' (length=6)
+//  'prenom' => string 'Robert' (length=6)
+//  'isMajeur' => boolean true
+//  'adresse' => array (size=2)
+//      'cp' => int 14000
+//      'ville' => string 'Caen' (length=4)
+```
+
+
 ### Depuis Doctrine
 
 Axios peut également prendre en entrée des requêtes Doctrine `Doctrine\ORM\Query`.
diff --git a/js/Client/flashMessenger.js b/js/Client/flashMessenger.js
index e06b920d3e8b327a7afe521190a00dc345d7813a..049dc592521dc3ccac408d0789040fed42ffcc94 100644
--- a/js/Client/flashMessenger.js
+++ b/js/Client/flashMessenger.js
@@ -39,8 +39,8 @@ function toast(message, severity) {
     toast.setAttribute('aria-live', 'assertive');
     toast.setAttribute('aria-atomic', 'true');
 
-    if (severity === 'error'){
-        toast.setAttribute('style', 'width:100%');
+    if (severity === 'error' && message.length > 500){
+        toast.setAttribute('style', 'width:700px');
     }
 
     const toastContent =
@@ -57,7 +57,7 @@ function toast(message, severity) {
     if (severity !== 'error') {
         setTimeout(() => {
             toast.classList.remove('show');
-        }, 3000);
+        }, 5000);
     }
 }
 
diff --git a/js/Client/unicaenVue.js b/js/Client/unicaenVue.js
index 24b8f989b87757ed7643eaba52aea06a340517ea..471090325852e3d2c720e94747f22cf081cb5f13 100644
--- a/js/Client/unicaenVue.js
+++ b/js/Client/unicaenVue.js
@@ -83,11 +83,27 @@ unicaenVue.axios.interceptors.response.use(response => {
 
     return response;
 }, (error) => {
-    var text = $("<div>").html(error.response.data);
+    let message = error.response.data;
 
-    text.find('i.fas').hide();
+    if (error.response.status == 403){
+        message = '<h4>403 - Accès interdit</h4><br />Vous n\'êtes pas autorisé(e) à faire cette action.';
+    }else if (error.response.status == 500) {
+        const text = document.createElement("div");
+        text.innerHTML = error.response.data;
 
-    flashMessenger.toast(text.find('.alert').html(), 'error');
+        // on masque l'icône /!\ qui fait doublon si on en trouve
+        const fasIcons = text.querySelectorAll('i.fas');
+        fasIcons.forEach(icon => icon.style.display = "none");
+
+        message = text.querySelector('.alert').innerHTML;
+        if (message === undefined){
+            message = error.response.data;
+        }
+    }else{
+        message = error.response.data;
+    }
+
+    flashMessenger.toast(message, 'error');
 });
 
 unicaenVue.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
diff --git a/src/Axios/AxiosExtractor.php b/src/Axios/AxiosExtractor.php
index 43f5285706dfcbf9f1497ac448f99497d0fda11b..8eaa0ba9f6cd40a6fcd3082ea99a0d24430eabf5 100644
--- a/src/Axios/AxiosExtractor.php
+++ b/src/Axios/AxiosExtractor.php
@@ -9,36 +9,49 @@ class AxiosExtractor
 {
     const DATETIME_FORMAT = 'Y-m-d\TH:i:s.u\Z'; // timestamp ISO 8601 pour HTML5
 
+    protected array $triggers = [];
 
-    public static function extract($data, array $properties = [])
+
+    public static function extract($data, array $properties = [], array $triggers = [])
     {
         $axios = new self;
+        $axios->triggers = $triggers;
 
         return $axios->extractData($data, $properties);
     }
 
 
 
-    public function extractData($data, array $properties = [])
+    protected function extractData($data, array $properties = [], string $path = '')
     {
+        $triggerPath = $path ? $path : '/';
         if ($data instanceof Query) {
-            return $this->extractData($data->getResult(), $properties);
+            $result = $this->extractData($data->getResult(), $properties, '');
         } elseif ($this->isList($data)) {
-            return $this->extractList($data, $properties);
+            $result = $this->extractList($data, $properties, $path);
+            $triggerPath .= '[]';
         } elseif (is_array($data)) {
-            return $this->extractArray($data, $properties);
+            $result = $this->extractArray($data, $properties, $path);
         } elseif ($data instanceof \DateTime) {
-            return $data->format(self::DATETIME_FORMAT);
+            $result = $data->format(self::DATETIME_FORMAT);
         } elseif (is_object($data)) {
-            return $this->extractObject($data, $properties);
+            $result = $this->extractObject($data, $properties, $path);
         } else {
-            return $data;
+            $result = $data;
+        }
+
+        if (array_key_exists($triggerPath, $this->triggers) && !$data instanceof Query){
+            // trigger est un callable qui accepte deux arguments : le premier les la donnée originale, le second la donnée extraite
+            // il doit retourner une donnée qui remplacera la donnée extraite
+            $result = $this->triggers[$triggerPath]($data, $result);
         }
+
+        return $result;
     }
 
 
 
-    protected function extractObject($data, array $properties): array
+    protected function extractObject($data, array $properties, string $path = ''): array
     {
         $result = [];
 
@@ -81,7 +94,7 @@ class AxiosExtractor
             foreach ($methods as $method) {
                 if (method_exists($data, $method)) {
                     $value             = $data->$method();
-                    $result[$property] = $this->extractData($value, $subProperties);
+                    $result[$property] = $this->extractData($value, $subProperties, $path.'/'.$property);
                     break;
                 }
             }
@@ -97,7 +110,7 @@ class AxiosExtractor
 
 
 
-    protected function extractArray(array $data, array $properties): array
+    protected function extractArray(array $data, array $properties, string $path = ''): array
     {
         $result = [];
 
@@ -121,7 +134,7 @@ class AxiosExtractor
             }
 
             if (array_key_exists($property, $data)) {
-                $result[$property] = $this->extractData($data[$property], $subProperties);
+                $result[$property] = $this->extractData($data[$property], $subProperties, $path.'/'.$property);
             }
         }
 
@@ -130,11 +143,11 @@ class AxiosExtractor
 
 
 
-    protected function extractList($list, array $properties = []): array
+    protected function extractList($list, array $properties = [], string $path = ''): array
     {
         $result = [];
         foreach ($list as $sobj) {
-            $result[] = $this->extractData($sobj, $properties);
+            $result[] = $this->extractData($sobj, $properties, $path);
         }
 
         return $result;
diff --git a/src/View/Model/AxiosModel.php b/src/View/Model/AxiosModel.php
index 30dac8e960a8ab91d7c9b4fd8868ddc55a3f743e..2a2d0fb22b34ffdba5749327ce40fe8b20a7f58a 100644
--- a/src/View/Model/AxiosModel.php
+++ b/src/View/Model/AxiosModel.php
@@ -12,12 +12,15 @@ class AxiosModel implements ModelInterface
 
     protected array $properties = [];
 
+    protected array $triggers = [];
 
 
-    public function __construct($data, array $properties = [])
+
+    public function __construct($data, array $properties = [], array $triggers = [])
     {
         $this->data       = $data;
         $this->properties = $properties;
+        $this->triggers   = $triggers;
     }
 
 
@@ -54,6 +57,22 @@ class AxiosModel implements ModelInterface
 
 
 
+    public function getTriggers(): array
+    {
+        return $this->triggers;
+    }
+
+
+
+    public function setTriggers(array $triggers): AxiosModel
+    {
+        $this->triggers = $triggers;
+
+        return $this;
+    }
+
+
+
     public function getVariables()
     {
         return $this->data;
diff --git a/src/View/Renderer/AxiosRenderer.php b/src/View/Renderer/AxiosRenderer.php
index 53115afd39a6964340813039a7ab8c4a07ca8cb5..4b20f9557ca5cc2949efdcbcd4623fb1a51d2b0f 100755
--- a/src/View/Renderer/AxiosRenderer.php
+++ b/src/View/Renderer/AxiosRenderer.php
@@ -33,7 +33,7 @@ class AxiosRenderer extends JsonRenderer
 
     protected function makeData(AxiosModel $axiosModel): array
     {
-        $data = AxiosExtractor::extract($axiosModel->getData(), $axiosModel->getProperties());
+        $data = AxiosExtractor::extract($axiosModel->getData(), $axiosModel->getProperties(), $axiosModel->getTriggers());
 
         $namespaces = [
             $this->flashMessenger::NAMESPACE_SUCCESS,