blog ID-INFO
Les services web sur l’IBM i
Vous avez dû très probablement entendre parler des services web, mais de quoi s’agit-il exactement ?
En interrogeant votre moteur de recherche préféré, vous apprendrez qu’un service web est un protocole d’interface permettant à deux applicatifs de discuter entre eux. Ceux-ci reposent sur les technologies du web pour permettre à deux applicatifs, qui peuvent être totalement différents, d’échanger des informations.
De ce fait, les services web sont souvent employés dans des environnements distribués dans lesquels de nombreuses applications hétérogènes doivent communiquer entre elles. De plus, ces outils font écho aux principes d’architecture orientée services (SOA) que l’on rencontre de plus en plus fréquemment dans nos systèmes d’information et qui nous permettent d’être bien plus flexibles en termes de solutions que nous pouvons apporter.
De nos jours, les services web reposent sur le protocole HTPP (Hypertext Transfer Protocol) pour véhiculer les informations. L’utilisation de ce standard a l’énorme avantage d’éviter énormément de problèmes de connectivité entre nos deux applicatifs.
Les services web reposent sur un échange de type client/serveur. En pratique l’applicatif fournissant le service (on dira fréquemment exposant le service) reste à l’écoute d’éventuelles sollicitations déclenchées par les clients. La demande est interprétée par le serveur, traitée, puis la réponse est transmise à l’applicatif client sous une forme compréhensible par celui-ci.
Il existe 2 grandes familles de services web :
- Les services web SOAP : Simple Object Access Protocol
- Les services web REST : Representational State Transfer
Les premiers, reposent sur le protocole du même nom. Celui-ci existe de longue date : la version initiale définie par Microsoft et iBM date de 23 ans puis est devenu un standard suite à une recommandation du W3C. Le protocole s’appuie entièrement sur le langage XML pour décrire de manière formelle les échanges entre les clients et le serveur. Ce formalisme (via des documents XML nommés WSDL) a l’avantage de fournir une documentation implicite lorsqu’on utilise ce type de services. Malheureusement, la famille souffre de quelques inconvénients : le langage XML étant particulièrement verbeux, l’utilisation de ce type de web services peut rapidement devenir compliqué. D’autre part, le formalisme implique que les clients et le serveur soient fortement couplés ce qui, dans une démarche SOA dans laquelle on cherche toujours plus de flexibilité, n’est pas forcément souhaitable.
Ces inconvénients ont favorisé l’apparition de la famille des services web REST. Ceux-ci reposent sur les concepts architecturaux utilisés dans le web pour manipuler des ressources distantes sous forme textuelles. Contrairement aux services web SOAP, la famille des services web REST ne se limite pas qu’au format XML pour échanger des informations entre les deux applicatifs client / serveur : des formats comme le JSON sont aujourd’hui communément utilisés. Afin de manipuler les ressources distantes ces web services utilisent les méthodes http : POST, GET, PUT, DELETE, … qui font écho au modèle de développement CRUD ce qui explique qu’on utilise fréquemment ces web services pour construire des API (Application Programming Interface) complètes pour interagir avec les éléments d’un applicatif.
Mais qu’en est-il de notre IBM i ? Peut-il lui aussi s’inscrire dans ces environnements distribués ?
Je vous rassure tout suite, notre plateforme favorite dispose d’énormément d’outils afin d’exposer ou de consommer des services web. Comme de nombreuses autres plateformes, nous pouvons implémenter ces services de diverses manières et les mettre à disposition de nos divers clients.
Pour illustrer un peu nos propos mettons-nous dans la situation d’un applicatif existant développé en RPGIV.
Pour ce faire commençons par nous créer un environnement avec quelques tables et données. Fort heureusement pour nous, IBM nous a fourni une procédure stockée SQL pour cela en une ligne :
CALL QSYS.CREATE_SQL_SAMPLE(‘RHDB’); |
Ouvrons donc notre outil d’exécution de script SQL dans ACS et lançons-nous.
Comme vous l’aurez deviné au nom de la collection créée, nous allons travailler sur des données de ressources humaines.
Un petit tour dans l’explorateur de base de données, nous indique les tables créées.
Attardons-nous sur la table EMPLOYEE et écrivons un petit programme pour récupérer l’information relative à un employé.
Imaginons que nous disposions d’un programme de service permettant d’extraire les informations relatives à un employé.
/if not defined(IMPORT_PROTOTYPES)
ctl-opt option ( *nodebugio : *srcstmt )
debug
nomain
pgminfo
(
*PCML
:
*MODULE
);
* Parametres SQL.
exec sql
set option commit
=
*none,
closqlcsr
=
*endmod
;
*======================================================================= *
* *
* ATTENTION : *
* *
* ---------------------------------------------------------------------- *
* FONCTION : Module RH. *
* DESCRIPTION : Manipulation donénes du personnel. *
* *
* Creation ... : 25/10/2021 par L. KIEFFER (Notos-Id Info) *
* ---------------------------------------------------------------------- *
* Modification : *
* JJ/MM/AAAA - XXXXXX XXXXXXX - XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX. *
* * * ---------------------------------------------------------------------- *
/endif
*----------------------------------------------------------------------
* Prototype verification sous systeme.
* ---------------------------------------------------------------------
// Information employé.
dcl-ds MODULERH_employe template qualified;
prenom varchar(12);
nom varchar(15);
service varchar(36);
end-ds;
// Recherche d'un employé.
dcl-pr MODULERH_rchEmp;
iNumEmp char( 6 ) const;
oInfEmp likeds ( MODULERH_employe );
oErrHTTP int(5);
oErrMsg varchar(40);
end-pr
;
* Fin des elements exportes.
/if defined(IMPORT_PROTOTYPES)
/eof
/endif
*----------------------------------------------------------------------
* Declarations globales
* ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Recherche d'employé.
// ---------------------------------------------------------------------
dcl-proc MODULERH_rchEmp export;
dcl-pi *n;
iNumEmp char( 6 ) const;
oInfEmp likeds ( MODULERH_employe );
oErrHTTP int(5);
oErrMsg varchar(40);
end-pi
;
// Recherche de l'employé.
exec sql
Select FIRSTNME, LASTNAME, DEPTNAME
into :oInfEmp.prenom, :oInfEmp.nom, :oInfEmp.service
From EMPLOYEE
Join DEPARTMENT on DEPTNO = WORKDEPT
Where EMPNO = :iNumEmp;
if sqlCode < 0;
oErrHTTP = 500;
oErrMsg = 'Erreur recherche de l''employé.';
return;
endif;
if sqlCode = 100;
oErrHTTP = 404;
oErrMsg = 'Employé inconnu.';
return;
endif;
// Tout s'est bien passé.
oErrHTTP = 200;
oErrMsg = *blanks;
return;
end-proc;
Notez les instructions en surbrillance, nous y reviendrons plus tard lors de l’exposition de notre service web.
Pour continuer nous allons avoir besoin d’un serveur de web services sur lequel seront hébergés nos services. Fort heureusement pour nous, IBM nous en fournit un sur l’IBM i : IWS (Integrated Web Services server).
Pour y accéder utilisez l’url suivante : http://mon_ibm_i:2001/HTTPAdmin
Où mon_ibm_i est l’adresse IP de votre partition ou son nom DNS.
ATTENTION : Si la page ne s’affiche pas, c’est probablement car l’application d’administration web n’est pas correctement démarrée. Lancez la commande suivante sur une ligne de commande 5250 :
STRTCPSVR SERVER(*HTTP) HTTPSVR(*ADMIN) |
Identifiez-vous à l’aide d’un profil ayant le droit *IOSYSCFG.
Dans la nouvelle page qui s’affiche, cliquez sur le lien « Create Web Services Server » et laissez-vous guider par l’assistant.
Nommez votre serveur à votre convenance. Par défaut une instance Apache sera créé.
Choisissez les ports utilisés par le serveur.
Vous avez la possibilité de customiser le travail serveur en lui fournissant une jobd spécifique.
De même vous pourrez choisir le profil utilisateur utilisé par le travail serveur. Pensez à bien définir celui-ci. Une bonne idée étant de définir ce profil au profil propriétaire de la base de données que vous allez manipuler. Attention si vous choisissez l’utilisateur par défaut QWSERVICE, vous devrez lui affecter un mot de passe.
Enfin, validez la création du serveur en cliquant sur le bouton « Finish »
Maintenant que nous disposons d’un serveur, exposons notre premier service. Pour ceci, sélectionnez le serveur créé précédemment et cliquez sur le lien « Manage Deployed Service ».
Cliquez sur le bouton « Manage Deployed Service ». Puis sur le bouton « Deploy ».
La première page de l’assistant vous permet de sélectionner l’exécutable implémentant votre service. Depuis la V7.3, vous pouvez également utiliser le langage sql pour implémenter le service.
Pour ce qui est des implémentations en RPG IV, seuls les composants exécutables peuvent être sélectionnés (programmes et programmes de service). Dans notre cas choisissons notre programme de service MODULERH.
L’écran suivant nous permettra de choisir un nom de ressource. Dans notre cas, appelons là « employes » puisque nous manipulons des données employés.
La page suivante nous permet de limiter l’accès au service web qu’à travers un port sécurisé (via SSL). Comme nous n’avons pas sécurisé notre serveur http répondons « No ».
De même nous pouvons demander une authentification pour ce service web.
Dans ce cas, seule une authentification « basique » est disponible. Il faudra dans ce cas que le client fournisse dans l’entête http « Authorization » un utilisateur et mot de passe valide sur le système.
L’étape suivante dans le cas de programmes de service vous permettra de sélectionner la procédure exportée implémentant le service (ici modulerh_rchemp).
L’assistant vous demandera par la suite comment il doit notifier le client que l’exécution du service s’est passée comme prévu ou avec une erreur. Vous pourrez ici agir sur les codes retours et messages d’erreur par défaut.
La page suivante est la plus importante. Elle permet de décrire comment les clients accéderons à votre service.
On pourra définir :
- Le verbe http utilisé (ici GET)
- Le modèle d’url utilisé pour l’accès
Celui-ci permet notamment de définir où sont placés nos paramètres lorsqu’ils sont directement fournis dans l’URL. La syntaxe à utiliser sera : /{nom_parametre}
Veuillez noter que vous pouvez également ajouter à la syntaxe une expression régulière pour contrôler la validité du paramètre saisi (ici \d+ pour une chaine numérique d’au moins un chiffre).
- Une variable en sortie permettant d’alimenter le code http en retour (doit être définie en tant qu’entier dans le RPG implémentant le service).
- Une variable alphanumérique en sortie venant se substituer au message d’erreur par défaut.
- Les types de données acceptées en entrée (*All correspondant à tous les types gérés : xml, json, données de formulaire, … => dans ce cas, le client devra obligatoirement préciser dans quel type d’information il fournit pour que le serveur le comprenne).
- Les types de données retournées : l’outil permet de renvoyer des données au format xml ou json. Là encore si le serveur sait fournir les deux, le client devra préciser lequel il préfère.
- Comment sont présentées les données en entrée et en sortie : encapsulées ou nom.
Enfin cet écran vous permet d’indiquer d’où seront récupérés les paramètres en entrée. Dans notre cas de l’url en utilisant l’identifiant num (rappelez-vous ces celui défini dans le modèle d’url).
L’écran suivant vous permettra de définir un utilisateur spécifique à l’exécution de ce web service. Le profil général au serveur devra avoir accès à ce profil spécifique au web service.
L’écran suivant permettra de définir les bibliothèques à ajouter à l’environnement de l’exécution.
L’avant dernier écran vous permet de configurer les éléments à passer au programme implémentant le service.
Enfin le dernier écran récapitule les éléments saisis. En cliquant sur le bouton « Finish », le web service est déployé et démarré.
Mais comment tester notre fameux web service me direz-vous ?
Le service web utilisant le verbe GET, un simple navigateur nous permettra de tester celui-ci.
Pour des cas un peu plus complexes, vous avez la possibilité de vous orienter vers des outils plus complets comme :
- CURL (si les lignes de commandes ne vous rebutent pas) : celui-ci vous permettra de paramétrer complètement votre requête (authentification, entêtes http, …).
- PostMan : outil graphique tout aussi complet que CURL.
Un petit exemple avec PostMan : ajoutons un entête http indiquant que nous ne souhaitons que du JSON pour la réponse (rappelez-vous notre service web peut fournir du xml ou du json).
Et voilà le ‘R’ de notre API CRUD est maintenant en place, il ne vous reste « plus » qu’à créer les autres procédures implémentant les fonctions manquantes. N’oubliez pas l’importance des verbes http lors de la mise en place de tout ceci :
C => POST
R => READ
U => PUT
Et D => DELETE.
J’espère que ce billet a pu clarifier un peu ce sujet. Nous vous disons à une prochaine fois, où nous pourrons nous projetter dans la peau du client et non plus du serveur.
Vous avez des questions concernant les services web ou votre plateforme IBM i ? Alors contactez-nous au 01 88 32 12 34, ou via le formulaire de contact.