25-11-2018, 01:18 PM
Salut,
Je suis allé tapper au pif de duckduckgo et je suis tombé sur ça https://docs.spring.io/spring-statemachi...dling.html dont le principe semble dire "on laisse une chance au client de gérer l'erreur et s'il ne la gère pas, alors on la gère sur le serveur".
Maintenant, je vais parler en terme pragmatiques, pour mon cas perso.
Si je fais le parallèle entre une "state machine" et ma petite archi perso pour Variispace, le principe est simple: la "machine" (le jeu) est dans un état (dicté par la BDD) qui lui fait accepter certaines requêtes (messages) ou non.
Une requête n'est pas acceptable dans deux situations: elle est mal formée (l'event est incomplet) ou elle est mal venue (l'event n'est pas acceptable pour l'instant). On pourrait envisager un 3e cas, "l'event n'est pas compris", mais je le dispatch dans les deux autres: la machine *doit* comprendre tout event qu'elle reçoit (toute requête HTTP), et ranger dans "mal formé" les events incompris ("player_balalaika" n'est pas compris par ta machine dans ton exemple: c'est un event mal formé).
Dans le cas d'une malformation, la machine va directement dire au client que le message n'est pas acceptable. Soit dans son ensemble (parce que l'endpoint HTTP n'existe pas, donc en gros, "404, point, j'peux pas t'aider plus") soit en partie (parce qu'il manque un paramètre GET/POST ou parce qu'un de ces paramètres n'est pas du bon type, auquel cas le client est informé que tel élément du message n'est pas bon).
Cela, je l'ai géré simplement: la machine "endpoint" qui traite la requête jette une exception décrivant son problème, qui est attrapée par le serveur (pas par le client, c'est là ou soit je diverge de Spring soit je n'ai pas compris ce que Spring dit dans ce lien), qui la renvoie à un autre endpoint dédié à la gestion de cette erreur (et ce endpoint formatte l'erreur suivant le header Accept, et la renvoie au client, charge à lui de se démerder).
Dans le cas d'un event (requête) valide, alors le serveur web n'a rien à dire: il passe la demande à la BDD (à une procédure stockée, à laquelle il envoie les paramètres de la demande en rajoutant, souvent l'ID du joueur issu de la session) et le serveur de BDD procède pareil: il récupère la demande, et la traite: si elle est mal formée (procédure pas existante ou donnée par valide), MySQL jettera une exception (le serveur MySQL ne la traite pas, elle sera donc renvoyée au "client" qui est ici le serveur web, qui lui, la traitera comme un event "mal formé": il jettera une exception, catchée un peu plus haut dans le PHP, puis passée à un endpoint dédié au formattage de cette erreur, qui renvoie une erreur 500 sans trop d'infos pour ne rien laisser fuiter sur "l'état de la machine" aka du jeu); si elle est bien formée, alors il traite la demande puis renvoie les result sets au serveur web, qui les traite (et les formatte en HMTML, JSON ou autre suivant le HTTP Accept du client).
Toutefois, la requête reçue par MySQL (l'appel de procédure stockée) peut être bien formé, mais pas acceptable dans l'état du jeu (ie: je veux déplacer une flotte pas à moi, ou qui est déjà détruite, ou construire un vaisseau sans avoir les ressources, etc). En ce cas, MySQL jette une exception (SIGNAL) de manière explicite (type IF (NOT EXISTS(SELECT 1 FROM fleet WHERE id_player = idPlayer AND id = idFleet)) THEN SIGNAL... END IF où idPlayer et idFleet sont des paramètres de la procédure stockée désignant le joueur et la flotte à déplacer) car c'est une règle métier, soit il la jette de manière implicite car le modèle de données le rejette (type INSERT INTO player (pseudo, mail) VALUES (paramPseudo, paramMail) qui génère automatiquement une DUPLICATE KEY si le joueur qui veut s'inscrire utilise un mail/pseudo déjà pris).
Dans tous les cas, MySQL "tente" de gérer l'exception (y'a pas de HANDLER généralement, donc il ne fait en fait rien) puis il cascade l'erreur à son client (le serveur web) qui tente alors lui-aussi de traiter le message (via les formatters dédiés aux exceptions).
Au final:
- Si t'exploses la machine, alors tu ne vas pas plus loin, et c'est dangereux: un client peut couper le jeu en forgeant une mauvaise requête, qui stoppe la machine
- Si tu ne fais rien du tout, alors il te seras très très compliqué de déboguer, et sachant que les erreurs en prod ça arrive, les joueurs seront comme deux ronds de flan à ne pas comprendre pourquoi rien ne se passe (et je te paries qu'ils cliqueront alors 30x sur les boutons et ragequiteront)
- Le mieux me semble donc d'être "je reste dans l'etat valide" (pour continuer mon cycle de vie) mais je notifie le client qu'il a fait de la merde et que son message n'est pas accepté (avec ou sans les raisons de cette acceptation, suivant la criticité de cette information).
En espérant ne pas avoir répondu à côté
Je suis allé tapper au pif de duckduckgo et je suis tombé sur ça https://docs.spring.io/spring-statemachi...dling.html dont le principe semble dire "on laisse une chance au client de gérer l'erreur et s'il ne la gère pas, alors on la gère sur le serveur".
Maintenant, je vais parler en terme pragmatiques, pour mon cas perso.
Si je fais le parallèle entre une "state machine" et ma petite archi perso pour Variispace, le principe est simple: la "machine" (le jeu) est dans un état (dicté par la BDD) qui lui fait accepter certaines requêtes (messages) ou non.
Une requête n'est pas acceptable dans deux situations: elle est mal formée (l'event est incomplet) ou elle est mal venue (l'event n'est pas acceptable pour l'instant). On pourrait envisager un 3e cas, "l'event n'est pas compris", mais je le dispatch dans les deux autres: la machine *doit* comprendre tout event qu'elle reçoit (toute requête HTTP), et ranger dans "mal formé" les events incompris ("player_balalaika" n'est pas compris par ta machine dans ton exemple: c'est un event mal formé).
Dans le cas d'une malformation, la machine va directement dire au client que le message n'est pas acceptable. Soit dans son ensemble (parce que l'endpoint HTTP n'existe pas, donc en gros, "404, point, j'peux pas t'aider plus") soit en partie (parce qu'il manque un paramètre GET/POST ou parce qu'un de ces paramètres n'est pas du bon type, auquel cas le client est informé que tel élément du message n'est pas bon).
Cela, je l'ai géré simplement: la machine "endpoint" qui traite la requête jette une exception décrivant son problème, qui est attrapée par le serveur (pas par le client, c'est là ou soit je diverge de Spring soit je n'ai pas compris ce que Spring dit dans ce lien), qui la renvoie à un autre endpoint dédié à la gestion de cette erreur (et ce endpoint formatte l'erreur suivant le header Accept, et la renvoie au client, charge à lui de se démerder).
Dans le cas d'un event (requête) valide, alors le serveur web n'a rien à dire: il passe la demande à la BDD (à une procédure stockée, à laquelle il envoie les paramètres de la demande en rajoutant, souvent l'ID du joueur issu de la session) et le serveur de BDD procède pareil: il récupère la demande, et la traite: si elle est mal formée (procédure pas existante ou donnée par valide), MySQL jettera une exception (le serveur MySQL ne la traite pas, elle sera donc renvoyée au "client" qui est ici le serveur web, qui lui, la traitera comme un event "mal formé": il jettera une exception, catchée un peu plus haut dans le PHP, puis passée à un endpoint dédié au formattage de cette erreur, qui renvoie une erreur 500 sans trop d'infos pour ne rien laisser fuiter sur "l'état de la machine" aka du jeu); si elle est bien formée, alors il traite la demande puis renvoie les result sets au serveur web, qui les traite (et les formatte en HMTML, JSON ou autre suivant le HTTP Accept du client).
Toutefois, la requête reçue par MySQL (l'appel de procédure stockée) peut être bien formé, mais pas acceptable dans l'état du jeu (ie: je veux déplacer une flotte pas à moi, ou qui est déjà détruite, ou construire un vaisseau sans avoir les ressources, etc). En ce cas, MySQL jette une exception (SIGNAL) de manière explicite (type IF (NOT EXISTS(SELECT 1 FROM fleet WHERE id_player = idPlayer AND id = idFleet)) THEN SIGNAL... END IF où idPlayer et idFleet sont des paramètres de la procédure stockée désignant le joueur et la flotte à déplacer) car c'est une règle métier, soit il la jette de manière implicite car le modèle de données le rejette (type INSERT INTO player (pseudo, mail) VALUES (paramPseudo, paramMail) qui génère automatiquement une DUPLICATE KEY si le joueur qui veut s'inscrire utilise un mail/pseudo déjà pris).
Dans tous les cas, MySQL "tente" de gérer l'exception (y'a pas de HANDLER généralement, donc il ne fait en fait rien) puis il cascade l'erreur à son client (le serveur web) qui tente alors lui-aussi de traiter le message (via les formatters dédiés aux exceptions).
Au final:
- Si t'exploses la machine, alors tu ne vas pas plus loin, et c'est dangereux: un client peut couper le jeu en forgeant une mauvaise requête, qui stoppe la machine
- Si tu ne fais rien du tout, alors il te seras très très compliqué de déboguer, et sachant que les erreurs en prod ça arrive, les joueurs seront comme deux ronds de flan à ne pas comprendre pourquoi rien ne se passe (et je te paries qu'ils cliqueront alors 30x sur les boutons et ragequiteront)
- Le mieux me semble donc d'être "je reste dans l'etat valide" (pour continuer mon cycle de vie) mais je notifie le client qu'il a fait de la merde et que son message n'est pas accepté (avec ou sans les raisons de cette acceptation, suivant la criticité de cette information).
En espérant ne pas avoir répondu à côté
![Wink Wink](https://jeuweb.org/images/smilies/wink.png)