31. Clients web pour les services jSON et XML de la version 12
Nous allons écrire trois applications console clientes des services jSON et XML du serveur web que nous venons d’écrire. Nous reprenons l’architecture client / serveur de la version 11 :

Nous écrirons trois scripts console :
- les scripts [main] et [main3] utiliseront la couche [métier] du serveur ;
- le script [main2] utilisera la couche [métier] du client ;
31.1. L’arborescence des scripts des clients
Le dossier [http-clients/07] est obtenu initialement par recopie du dossier [http-clients/06]. Il est ensuite modifié.

- en [1] : les données exploitées ou créées par le client ;
- en [2], la configuration et les scripts console du client ;
- en [3], la couche [dao] du client ;
- en [4], le dossier des tests de la couche [dao] du client ;
31.2. La couche [dao] des clients


31.2.1. Interface
La couche [dao] implémentera l’interface [InterfaceImpôtsDaoWithHttpSession] suivante :
Chaque méthode de l’interface correspond à une URL de service du serveur de calcul de l’impôt.
- ligne 7 : l’interface étend la classe [AbstractDao] qui gère les accès au système de fichiers ;
La correspondance entre les méthodes et les URL de service est faite dans le fichier de configuration [config] :
31.2.2. Implémentation
L’interface [InterfaceImpôtsDaoWithHttpSession] est implémentée par la classe [ImpôtsDaoWithHttpSession] suivante :
- lignes 16-34 : le constructeur de la classe ;
- ligne 19 : la classe parent est initialisée ;
- lignes 21-28 : on mémorise certaines données de la configuration ;
- lignes 29-34 : on crée trois propriétés utilisées dans les méthodes de la classe ;
- lignes 36-82 : la méthode [get_response] factorise ce qui est commun à toutes les méthodes de la couche [dao] : l’envoi d’une requête HTTP et la récupération de la réponse HTTP du serveur ;
- lignes 38-42 : définition des 5 paramètres de la méthode [get_response] ;
- ligne 42 : on notera que parce que le serveur maintient une session, le client a besoin de lire / renvoyer des cookies ;
- lignes 44-46 : on vérifie qu’il y a bien une session en cours valide ;
- ligne 51 : cas du GET. On renvoie les cookies reçus ;
- ligne 54 : cas du POST. Celui-ci peut avoir deux types de paramètres :
- le type [x-www-form-urlencoded]. C’est le cas des URL [/calculer-impot] et [/authentifier-utilisateur]. On utilise alors le paramètre [data_value] reçu par la méthode ;
- le type [json]. C’est le cas de l’URL [/calculer-impots]. On utilise alors le paramètre [json_value] reçu par la méthode ;
Là également, le cookie de session est renvoyé.
- lignes 56-62 : si on est en mode [debug], la réponse du serveur est loguée. Ce log est important car il permet de savoir exactement ce qu’a renvoyé le serveur ;
- lignes 64-68 : selon que l’on est en mode json ou xml, la réponse texte du serveur est transformée en dictionnaire. Prenons l’exemple de l’URL [/init-session] :
La réponse jSON est la suivante :
La réponse XML est la suivante :
Le code des lignes 64-68 assure que dans les deux cas, on obtient dans [résultat] un dictionnaire avec les clés [action, état, réponse] ;
- lignes 70-72 : si la réponse contient des cookies on les récupère. Il faudra les renvoyer lors de la prochaine requête ;
- lignes 74-79 : si le statut HTTP de la réponse est différent de 200, on lève une exception avec le message d’erreur contenu dans résultat[’réponse’]. Ce peut être une erreur ou une liste d’erreurs ;
- lignes 81-82 : on rend la réponse du serveur au code appelant ;
[init_session]
- ligne 84 : la méthode [init_session] sert à fixer le type de session json ou xml que veut commencer le client avec le serveur ;
- ligne 86 : on note le type de session désiré au sein de la classe. En effet, toutes les méthodes ont besoin de cette information pour décoder correctement la réponse du serveur ;
- lignes 88-90 : à l’aide de la configuration de l’application, on détermine l’URL de service qu’il faut interroger ;
- ligne 93 : l’URL de service est interrogée. On ne récupère pas le résultat de la méthode [get_response] :
- si elle lance une exception, alors l’opération a échoué. L’exception n’est pas gérée ici et remontera directement au code appelant qui arrêtera alors le client avec un message d’erreur ;
- si elle ne lance pas d’exception, alors c’est que l’initialisation de la session a réussi ;
[authenticate_user]
- la méthode [authenticate_user] sert à s’authentifier auprès du serveur. Pour cela elle reçoit les identifiants de connexion [user, password] en ligne 1 ;
- lignes 2-4 : on détermine l’URL de service à interroger ;
- lignes 5-8 : les paramètres du POST puisque l’URL [/authentifier-utilisateur] attend un POST avec des paramètres [user, password] ;
- ligne 11 : la requête est exécutée. Là encore on ne récupère pas la réponse du serveur. C’est l’exception lancée par [get_response] qui indique si on a réussi ou pas ;
[calculate_tax]
- la méthode [calculate_tax] permet de calculer l’impôt d’un contribuable [taxpayer] passé en paramètre. Ce paramètre est modifié par la méthode (ligne 15) et constitue donc le résultat de la méthode ;
- lignes 2-4 : on définit l’URL de service à interroger ;
- lignes 6-10 : les paramètres du POST à émettre. En effet, l’URL de service [/calculer-impot] attend un POST avec les paramètres [marié, enfants, salaire] ;
- lignes 12-13 : la requête est exécutée et la réponse du serveur récupérée. L’URL de service [/calculer-impot] renvoie un dictionnaire avec les clés [impôt, décôte, surcôte, réduction, taux] de l’impôt ;
- ligne 15 : le dictionnaire [response] obtenu est utilisé pour mettre à jour le contribuable [taxpayer] ;
[calculate_tax_in_bulk_mode]
- ligne 2 : la méthode reçoit une liste de contribuables de type TaxPayer ;
- lignes 7-13 : cette liste d’élements de type [TaxPayer] est transformée en liste de dictionnaires [marié, enfants, salaire] ;
- lignes 15-17 : on fixe l’URL de service ;
- lignes 19-20 : on exécute une requête POST dont le corps jSON est formé de la liste des dictionnaires créée ligne 7. On récupère la réponse du serveur ;
- lignes 23-24 : les tests ont montré un problème lorsque la session est de type XML :
- si la liste initiale des contribuables a N éléments (N>1) on obtient comme résultat une liste de N dictionnaires de type [OrderedDict] ;
- si la liste initiale n’a qu’un élément, on obtient non pas une liste mais un élément de type [OrderedDict] ;
lignes 23-24 : si on est dans ce dernier cas (1 élément), on transforme le résultat en liste de 1 élément ;
- lignes 25-28 : cette liste de dictionnaires reçus contient l’impôt de chaque contribuable de la liste initiale. On met à jour alors chacun d’eux avec les résultats reçus ;
[get_simulations]
- ligne 1 : la méthode demande la liste des simulations faites dans la session courante ;
- ligne 2 : la méthode rend la réponse du serveur ;
[delete_simulation]
- ligne 1 : la méthode supprime la simulation dont on passe l’identifiant ;
- ligne 7 : elle rend la réponse du serveur, la liste des simulations restantes après la suppression demandée ;
[get-admindata]
- ligne 1 : la méthode demande au serveur les constantes fiscales permettant le calcul de l’impôt ;
- ligne 29 : elle rend un type [AdminData] ;
- ligne 9 : on récupère la réponse du serveur sous la forme d’un dictionnaire. Les tests montrent qu’il y a un problème lorsque la session est une session XML : au lieu d’être des valeurs numériques, les valeurs du dictionnaire sont des chaînes de caractères. Nous avions signalé ce problème lors de l’étude du module [xmltodict] et constaté que c’était un fonctionnement normal. [xmltodict] n’a pas d’informations de types dans le flux XML qu’on lui donne. Ceci dit, dans ce cas précis, il faut convertir en numérique toutes les valeurs du dictionnaire reçu. Celui-ci contient trois listes [limites, coeffr, coeffn] et une suite de propriétés numériques ;
- lignes 13-25 : création d’un dictionnaire [résultat2] à valeurs numériques à partir du dictionnaire [résultat] à valeurs de type chaîne de caractères ;
- ligne 29 : le dictionnaire [resultat2] est utilisé pour initialiser un type [AdminData] ;
31.2.3. La factory de la couche [dao]
Nos clients seront multi-threadés. Comme la couche [dao] est implémentée par une classe ayant un état en lecture / écriture (= des propriétés en lecture / écriture), chaque thread doit avoir sa propre couche [dao] ou alors il faut synchroniser l’accès aux données partagées entre les threads. Ici nous prenons la première solution. Nous utilisons une classe [ImpôtsDaoWithHttpSessionFactory] capable de créer des instances de la couche [dao] :
31.3. Configuration des clients

Les clients sont configurés par les fichiers [config] et [config_layers]. Le fichier [config] est le suivant :
Le fichier [config_layers] est le suivant :
- les clients n’auront pas un accès direct à la couche [dao]. Pour en avoir une, ils devront passer par la factory de la couche [dao] ;
31.4. Le client [main]
Le client [main] permet de tester les URL [/init-session, /authentifier-utilisateur, /calculer-impots, /fin-session] :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | |
- lignes 4-11 : le client attend un paramètre lui indiquant le type de session, json ou xml, à utiliser avec le serveur ;
- lignes 13-15 : le client est configuré ;
- lignes 48-104 : ce code est connu. Il a été utilisé de nombreuses fois. Il répartit les contribuables pour lesquels on veut calculer l’impôt sur plusieurs threads ;
- ligne 26 : la méthode [thread_function] est la méthode exécutée par chaque thread pour calculer l’impôt des contribuables qui lui ont été attribués ;
- lignes 27-30 : chaque thread a sa propre couche [dao] ;
- le calcul de l’impôt se fait en quatre étapes :
- lignes 37-38 : initialisation d’une session, json ou xml, avec le serveur ;
- lignes 39-40 : authentification auprès du serveur ;
- lignes 41-42 : calcul de l’impôt ;
- lignes 43-44 : fermeture de la session avec le serveur ;
Lorsqu’on exécute ce code en mode [json], on obtient les logs suivants :
On voit ci-dessus le parcours du thread [Thread-2].
Si on exécute [main] en mode XML, les logs sont les suivants :
Ci-dessus, le parcours du thread [Thread-2].
31.5. Le client [main2]

Le client [main2] permet de tester les URL [/init-session, /authentifier-utilisateur, /get-admindata, /fin-session] :
- lignes 1-11 : on récupère le paramètre [json, xml] qui fixe le type de la session à établir avec le serveur ;
- lignes 13-15 : on configure le client ;
- lignes 30-33 : on crée une couche [dao] ;
- lignes 34-35 : avec elle, on récupère la liste des contribuables pour lesquels il faut calculer l’impôt ;
- on retrouve ensuite les quatre étapes du dialogue avec le serveur ;
- lignes 41-42 : une session est démarrée avec le serveur ;
- lignes 43-44 : on s’authentifie auprès du serveur ;
- lignes 45-46 : on demande au serveur les constantes fiscales permettant le calcul de l’impôt ;
- lignes 47-48 : on ferme la session avec le serveur ;
- lignes 49-52 : avec ces constantes, on est capables de calculer l’impôt des contribuables à l’aide de la couche [métier] locale au client ;
- lignes 53-54 : on enregistre les résultats obtenus ;
Pour une session XML, les résultats sont les suivants :
31.6. Le client [main3]
Le client [main3] permet de tester les URL [/init-session, /calculer-impots, /get-simulations, /delete-simulation, /fin-session] :

- lignes 1-11 : on récupère le type de la session dans les paramètres du script ;
- lignes 13-15 : on configure l’application ;
- lignes 25-50 : on retrouve du code déjà expliqué à un moment ou à un autre ;
- lignes 51-52 : on demande la liste des simulations faites dans la session courante ;
- lignes 53-57 : on supprime une simulation sur deux ;
- lignes 58-59 : on termine la session ;
Lors d’une session jSON, les logs sont les suivants :
- ligne 6 : nous avons 11 simulations ;
- ligne 12 : après les différentes suppressions, il n’y en a plus que 5 ;
31.7. La classe de test [Test2HttpClientDaoWithSession]

La classe [Test2HttpClientDaoWithSession] teste la couche [dao] des clients de la façon suivante :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | |
- la couche [dao] envoie une requête au serveur, réceptionne sa réponse et la met en forme pour la rendre au code appelant. Lorsque le serveur envoie un réponse avec un code de statut différent de 200, la couche [dao] lance une exception. Aussi un certain nombre de tests consiste à savoir s’il y a eu exception ou pas ;
- lignes 9-18 : on initialise une session jSON. On ne doit pas avoir d’erreur ;
- lignes 20-29 : on initialise une session XML. On ne doit pas avoir d’erreur ;
- lignes 31-40 : on initialise une session avec un type incorrect. on doit avoir une erreur ;
- lignes 42-54 : on s’authentifie avec les bons identifiants. On ne doit pas avoir d’erreur ;
- lignes 56-68 : on s’authentifie avec des identifiants incorrects. On doit avoir une erreur ;
- lignes 70-92 : on fait un calcul d’impôt puis on demande la liste des simulations. On doit en avoir une. Par ailleurs on verifie que cette simulation contient bien l’impôt demandé ;
- lignes 94-119 : on fait une simulation qu’on supprime ensuite. Puis on essaie ensuite de supprimer une simulation alors qu’il n’y en a plus. On doit avoir une erreur ;
- lignes 121-137 : le test est lancé comme un script console classique ;
- lignes 122-124 : on configure l’application ;
- lignes 126-129 : on configure le logger. Cela nous permettra de suivre les logs ;
- lignes 131-133 : on instancie la couche [dao] qui va être testée ;
- lignes 135-137 : on lance les tests ;
Les résultats console sont les suivants :