XML et Services WEB



XML


Soit une liste des cours AF400 , en HTML :

<h3>Liste des cours AF400 (&copy; Volubis)</h3>
<ul>
<li>&pound;C01.120 <em>(module &pound;cursuspgm)</em> ESPACE ADRESSABLE UNIQUE</li>
<li>&pound;C01.130 <em>(module &pound;cursuspgm)</em> INIT OS/400 (notion d'objet, biblioth&egrave;que)</li>
<li>etc...</li>
</ul>

ce qui s'affiche :

Liste des cours AF400 (© Volubis)

imaginons maintenant la même liste XML

(voir le fichier complet, le source du pgm ayant réalisé cela)

Voici la manière dont RDP, montre ce fichier :

Les règles du jeu XML


Elles sont extrêmement simples. Les informations doivent être :

  un schéma est lui même un fichier XML qui en décrit un autre.

commençons par une partie d'entête :

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
<xsd:annotation>
<xsd:documentation xlm:lang="fr">
XML Schema pour la lsite des cours AF400.
</xsd:documentation>
</xsd:annotation>

puis la partie descriptive en elle même:

<xsd:element name="AF400" type="AF400Type"/> 

<xsd:complexType name="AF400Type">
<xsd:sequence>
<xsd:element name="COURS" type="CoursType" minOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="CoursType"> 
<xsd:sequence>
<xsd:element name="TEXTE" type="xsd:string"/>
<xsd:element name="TYPE" type="xsd:string"/>
<xsd:element name="SRCFIL" type="xsd:string"/>
<xsd:element name="SRCLIB" type="xsd:string"/>
<xsd:element name="SRCMBR" type="xsd:string"/>
<xsd:element name="CHEMIN" type="xsd:string"/>
<xsd:element name="SUJET" type="xsd:string"/>
<xsd:element name="MOT-DIRECTEUR type="MotType"/>
<xsd:element name="DATE" type="xsd:string"/>
</xsd:sequence>
< /xsd:complexType>
<xsd:complexType name="MotType"> 
<xsd:sequence>
<xsd:element name="MOTCLE1 type="xsd:string" minOccurs="1"/>
<xsd:element name="MOTCLE2 type="xsd:string"/>
<xsd:element name="MOTCLE3 type="xsd:string"/>
<xsd:element name="MOTCLE4 type="xsd:string"/>
<xsd:element name="MOTCLE5 type="xsd:string"/>
</xsd:sequence>
< /xsd:complexType>

On peut définir un élément (xsd:element) ou un attribut (xsd:attribut), chacun pouvant faire référence à un type  
(voyez ici la liste des types admis dans un schéma XML)  comme xsd:nonNegativeNumber
ou bien faire référence à un type définit dans le scéma.


Les types définis peuvent être :


Lorsqu'un document XML possède une DTD ou un Schéma associé et la/le respecte, on dit qu'il est valide.

Lorsqu'il respecte seulement les règles de la grammaire XML (balises fermées, correctement imbriquées) on dit qu'il est bien formé.



Aujourd'hui JSON est de plus en plus utilisé.

JSON (JavaScript Object Notation) est un format de données textuelles dérivé de la notation des objets du langage JavaScript (Wikipedia)

 

ce qui s'écrit comme cela en XML   s'écrit comme cela en JSON
<producteurs>
  <producteur>
    <numero>45</numero>
   <commune>Reims</commune>
    <appellation>13</appellation>
  </producteur>
</producteurs>
->

{
   "producteurs":  {
       "producteur": {
            "numero":45,
            "commune":"Reims",
            "appellation":13  
       }
    }
}


D'ailleurs vous trouverez ici deux fonctions bien pratiques XML2JSON et JSON2XML écrites en Java.

Un élément peut contenir un objet, une valeur ou un tableau de valeurs, marqué alors par [ et ]

<PO>
 <id>103</id>
<orderDate>2014-06-20</orderDate>
<customer>
<cid>888</cid>
</customer> <items>
<item>
<partNum>872-AA</partNum>
<shipDate>2014-06-21</shipDate>
<productName>Lawnmower</productName>
<USPrice>749.99</USPrice>
<quantity>1</quantity>
</item>
<item>
<partNum>837-CM</partNum>
<productName>Digital Camera</productName>
<USPrice>199.99</USPrice>
<quantity>2</quantity>
<comment>2014-06-22</comment>
</item>
</items>
</PO>

devient (remarquez le tableau de "item")

{
 "PO":{
 "id": 103, 
 "orderDate": "2014-06-20", 
 "customer": {"@cid": 888}, 
 "items": { 
   "item": [
             { "partNum": "872-AA", 
               "productName": "Lawnmower", 
               "quantity": 1, 
               "USPrice": 749.99, 
               "shipDate": "2014-06-21" 
             }, 
             { "partNum": "837-CM", 
               "productName": "Digital Camera", 
               "quantity": 2, 
               "USPrice": 199.99, 
               "comment": "2014-06-22" 
             } 
          ] 
        } 
    }
}

 


L'état de l'art est aujourd'hui de travailler dans une architecture dite 3 tiers, c'est à dire en découplant le serveur de traitement (les programmes souvent placés avec le serveur WEB) des données (pouvant être situées sur un serveur éloigné).

Cette technique est implémentée avec les serveurs d'application ou serveurs de servlet.
    Websphere Application Server(WAS) ou TOMCAT par exemple, mais aussi ".NET" de Microsoft.

 

Il s'agit d'écrire des programmes JAVA (ou C# ou PHP) s'exécutant sur le serveur et non sur le poste client , le serveur d'application assurant le lien entre le serveur WEB et la JVM (machine virtuelle java).Ces programmes java pouvant être des classes autonomes (servlet) générant du HTML :


ou contenus dans des pages JSP : pages HTML faisant références à des objets externes [des beans].

Les pages JSP permettant d'intégrer du HTML (conçu par un graphiste) et du code JAVA (écrit par un développeur) dans un même fichier.


Aujourd'hui, pour une application WEB, l'architecture logique doit être :


 Tout cela étant normalisé aujourd'hui sous le vocable générique de "Web services"

Définitions

liens :

Présentation de l'architecture web-services

(ou services web)


   

Il existe deux types de services WEB

Un web-service : c'est un logiciel qui interagis avec d'autres au moyen de protocoles universels (http, xml...)


Précisions sur SOAP


Précisions sur WSDL

 

Java et les web-services


Et sur nos systèmes IBM i?


IBM proposait déjà des mécanismes d'appel de programmes (RPG ou COBOL) depuis une application Java : PCML : langage de description du programme
<pcml version="1.0">
 <!-- Create a Data Structure -->
 <struct name ="custinfo" >
   < data name ="Number" type="char" length= " 7"
            usage="inputoutput" init="0014400"> </data>
   < data name ="Name"   type="char" length= " 40"
            usage="inputoutput" init=" "> </data>
 </struct>
 <!-- Program getcust -->
 <program name="getcust"
              path="/QSYS.lib/MABIB.lib/GETCUST.pgm" >
   < data name ="gotback" type=" struct"
            usage="inputoutput" struct="custinfo"> </data>
 </program >
</pcml>


public static void main(String[] argv)
{
       AS400 as400System = new AS400();
       ProgramCallDocument pcml = null;
       String msgId, msgText;
       Object value = null;
       try {
           System.out. println ("Creating ProgramCallDocument for GetCust pgm.");
           pcml = new ProgramCallDocument(as400System, "GETCUST");
           boolean ok = pcml.callProgram("getcust");
           System.out.println(" rc is---> " + rc);
           if (!ok)
               { /* Retrieve list of AS/400 messages & display them */ }
           else
               {
                  value = pcml.getValue("getcust.gotback.Name");
                  System.out.println("Customer name: " + value);
               }
       } catch (PcmlException exc) {
           System.out.println("*** Call to getcust failed. ***");
           Sy ste m. exit (0);
       }
       System.exit(0 );
} // end main method

WDS Client V7et RDI for SOA contiennent les mêmes fonctionnalités (mais avec un nouvel assistant pour RDI)

Il faut cliquez droit à partir d'un source RPG4 (ILE uniquement)

Indiquez que vous souhaitez générer un client de test.

 

Cliquez sur parcourir pour modifier les caractéristiques du service

1/ le choix *SRVPGM ou *PGM

2/ par le bouton Modifier, la connexion :

Vous devrez indiquer aussi, le(les) type(s) de service(s) à générer

il vous faudra démarrer le serveur WAS de test sur votre poste.

Confirmer le lancement du test

la page de test est générée puis lancée


Enfin, l'intégration de code-opération pour manipuler le XML et l'arrivée d'outils pour travailler sur les sockets (outils IBM ou projets indépendants comme iSockets  ou HTTPApi de Scott Klement ) permettent aujourd'hui de consommer des services WEB depuis un RPG dès la version 5.


voir les liens ci dessous :


IBM i V6R1 : Integreted Web Application Serveur V7.1

 

 

Il s'agit du nouveau serveur d'application intégré à l'OS pour distribuer les applications Java., remplaçant Tomcat, qui n'est plus fourni avec la V6 et en V5R40, via PTF

 

Ce serveur support JSF, JSP, servlets et services web, et implique peu de ressources et d'administration, c'est le même que celui utilisé par DB2 WebQUERY, il est basé sur OSGI, puis une version de WAS nommée Liberty profile

 

Lancez le serveur d'administration HTTP, si ce n'est déjà fait par STRTCPSVR *HTTP HTTPSVR(*ADMIN)

Puis loggez vous sur http://<votreas400>:2001/HTTPAdmin/


 

Suivant les versions, vous allez déployer votre premier service web en même temps que la création du serveur d'application ou dans un deuxième temps

Ici un programme W_RECAP de BDVIN0, attendant une zone PR_CODE, et retournant une DS nommée INFOCENTRE,
    ce pgm RPG a été compilé avec PGMINFO(*PCML:*MODULE) , en COBOL ->PROCESS OPTIONS PGMINFO(PCML MODULE)

en V5R40 il faut SI27065 pour activer cette option.

SI55531 (7.2) et SI55340 (7.1) apportent l'option *DCLCASE permettant de demander le respect de la casse quant aux noms dans le PCML



Si vos programmes sont dans un ASP indépendant, Utilisez plutôt l'option Browse...

Et indiquez /iASP/QSYS.LIB/mabib.LIB/monpgm.PGM ou monsrvpgm.SRVPGM

ou /iASP est le point de montage de votre ASP indépendant

Indiquez ensuite, le nom public du service et un texte explicatif

puis, précisez le sens d'utilisation des paramètres (automatiquement découverts par l'assistant grâce à PGMINFO)

Si vous avez oublié l'option PGMINFO ou si vous appelez un CL (CLLE et non CLP), il faudra faire référence à un fichier PCML, pour faire apparaitre les paramètres

SI vous voulez lancer un COBOL (non ILE) ou un RPG 3, faite un CLLE qui appelle pour vous.

un fichier PCML devant être structuré comme suit :

<pcml version="4.0"> 
   <program name="MONPGM" path="/QSYS.LIB/MABIB.LIB/MONPGM.PGM"> 
      <data name="P1" type="char"   length="3" usage="inputoutput" /> 
      <data name="P2" type="packed" length="2" precision="0" usage="inputoutput" /> 
   </program> 
 </pcml> 


Dans un ASP indépendant

<pcml version="4.0"> 
   <program name="MONPGM" path="/IASP/QSYS.LIB/MABIB.LIB/MONPGM.PGM"> 
      <data name="P1" type="char"   length="3" usage="inputoutput" /> 
      <data name="P2" type="packed" length="2" precision="0" usage="inputoutput" /> 
   </program> 
 </pcml> 

avec une structure :

<pcml version="4.0"> 
 <!-- exemple avec une structure complexe -->
 <struct name="INFOCENTRE">
   <data name="PR_CODE" type="int" length="4" precision="31" usage="inherit" /> 
   <data name="PR_NOM" type="char" length="50" usage="inherit" /> 
   <data name="PR_TEL" type="char" length="20" usage="inherit" /> 
   <data name="APPEL00001" type="char" length="80" usage="inherit" /> 
   <data name="NBVIN" type="packed" length="3" precision="0" usage="inherit" /> 
   <data name="ENCAVE" type="char" length="3" usage="inherit" /> 
   <data name="CEPAGE" type="char" length="25" usage="inherit" /> 
   <data name="NBCEPAGE" type="packed" length="3" precision="0" usage="inherit" />
 </struct> 
 <!-- le pgm avec ses paramètres -->
 <program name="AVECDS" path="/QSYS.LIB/MABIB.LIB/AVECDS.PGM">
     <data name="PR_CODE" type="packed" length="9" precision="0" usage="inputoutput" />
     <data name="INFOCENTRE" type="struct" struct="INFOCENTRE" count="500" usage="inputoutput" />
 </program> 
</pcml>

voici les types reconnus par PCML :  char | int | packed | zoned | float | byte | struct


Pour infos, voici un extrait du programme déployé ici, en ce qui concerne les paramètres :

 H PGMINFO(*PCML : *MODULE)
 DPR_code                          S              6  0    
 DINFOCENTRE                     E DS                  qualified   

                                                              
C *entry plist
C parm pr_code
C parm infocentre

En free

Ctl-opt PGMINFO(*PCML : *MODULE); Dcl-DS INFOCENTRE_T EXT EXTNAME('INFOCENTRE') Qualified Template; DCL-PI *n;
pr_code packed(6:0);
infocentre likeds(infocentre_T);
End-PI;

 

Les groupes PTF de 2009, apportent la possibilité de gérer en variable (integer uniquement) le nombre d'occurrences d'une structure

Enfin en Juin 2015, SI56823(7.2) propose de rechercher automatiquement le nombre d'occurrences dans une variable placée avant la DS et nommée comme la DS suivit de _LENGTH

si vous décochez l'option, vous retrouvez l'affichage du dessus permettant de saisir le nbr d'occurrences


Cela permet de gérer les DS imbriquées :

Indiquez ensuite le profil utilisateur qui lancera le programme

indiquez aussi, la liste des bibliothèques à utiliser



Si vous travaillez avec un ASP indépendant, vous saisirez /IASP/QSYS.LIB/AUTREBIB.LIB

et voilà, vérifiez les informations récapitulatives (regardez les ports IP attribués automatiquement)

liste des services (l'assistant créé un exemple nommé ConvertTemp)

Les opérations admises

soit les informations que l'on peut demander :

w_recap, les paramètres retour transmis dans l'enveloppe SOAP
w_recap_XML, les données transmises dans un flux XML

 

Le serveur se créé

Puis, vous basculez automatiquement sur la gestion du serveur d'application de type web services V1.3, cette fois.

Remarquez l'ajout automatique du service exemple convertTemp

Vous pouvez, ici, revenir gérer les services déployés, ou bien, en déployer de nouveaux. (Manage…).


(properties, permet de changer le profil ou d'éditer le fichier WSDL)

le WSDL pouvant être produit dynamiquement ou de manière statique (nouveauté) et donc être édité par vos soins

passez la paramètre "Web service endpoint generation" à Static, appliquez, puis éditer le wsdl.



Important si vous masquez votre Serveur IBM i derrière un nom de domaine public.

Il faudra alors remplacer (par exemple)

   <soap:address location="http://as400.volubis.intra:10030/web/services/W_RECAP"/>

par

   <soap:address location="http://www.volubis.fr:10030/web/services/W_RECAP"/>



Mais surtout, vous pourrez tester votre service web, par le bouton "Test Service"

Choisissez l'opération (w_recap ou w_recap_XML), ici w_recap

Ajoutez un paramètre en entrée, PR _CODE est automatiquement proposé et vous ne pourrez l'ajouter qu'une seule fois

puis ajoutez une valeur (là aussi, une seule occurrence, dans ce cas) et cliquez sur GO

la réponse vous est affichée dans la fenêtre Status

Ci dessous, le résultat de w_recap_XML

 

 

Vous pouvez tester aussi avec des produits standard comme soapUI

Installez et lancez le produit.

Créez un nouveau projet en indiquant les coordonnées pour acquérir le fichier WSDL sur le system i.

renseignez les valeurs envoyées en remplacant les ? par vos données (ici le producteur 45)

lancez (flèche verte en haut à gauche de la fenêtre Request1)


Si vous rencontrez des problèmes, voyez

Octobre 2008, une PTF PTF SI32432 apporte les scripts suivants qui à ce jour ne sont donc pas documentés, dans /Qibm/ProdData/os/webservices/V1/server/bin

createWebServicesServer.sh
deleteWebServicesServer.sh
getWebServiceProperties.sh
getWebServicesServerProperties.sh
installWebService.sh
listWebServices.sh
listWebServicesServers.sh
setWebServiceProperties.sh
setWebServicesServerProperties.sh
startWebService.sh
startWebServicesServer.sh
stopWebService.sh
stopWebServicesServer.sh
uninstallWebService.sh

 

pour le passage en production d'un service WEB, il faut passer en production l'objet *PGM (l'exécutable) et l'enregistrer ensuite par le script installWebService.sh

les derniers correctifs (2015) proposent un nouveau paramètre -parameterUsage

permettant d'indiquer le sens d'utilisation des paramètres i (input) , o (output) , io (input/output)


-parameterUsage GetClientDEp:i,o:UpdateClientCA:i,i,io

pour vous familiariser avec les scripts, passez la commande QSH (qui est un émulateur de shell Unix sur IBM i) sur une ligne de commande OS/400

puis CD /qibm/proddata/os/webservices/v1/server/bin


ls vous affiche le contenu du répertoire, pwd vous affiche le répertoire en cours, etc...


si vous tapez le nom d'un script -help , il vous affiche une aide "limitée" , par exemple :
installWebservice.sh -help 
     
Command usage: 

installWebService.sh -server 'server-name' -programObject 'program-object'
[-service 'service-name'] [-pcml 'pcml-file'] [-userid 'userid']
[-detectFieldLengths] [-serviceType '*SOAP11|*SOAP12|*REST']
[-targetNamespace 'target-namespace']
[-parameterUsage 'parameter-list'] [-propertiesFile 'property-file']
[-libraryList 'library-list'] [-libraryListPosition '*FIRST|*LAST']
[-disableNillableWSDLElements] [-disableOptionalWSDLElements]
[-addUnderscoreToWSDLElementNames] [-printErrorDetails] [-help]


Quelques Exemples :

installWebService.sh
   -server 'WFORMATION'
   -programObject '/QSYS.LIB/FORMATIONX.LIB/GETVINS.PGM'
   -service 'GETVINJ'   -userid 'CM' -detectFieldLengths
   -serviceType '*REST' -propertiesFile '/home/CM/getvins.properties'
   -libraryList 'FORMATIONX;BDVIN1' -libraryListPosition '*LAST' 

fichier properties :
uri.path.template=/
GETVINS.uri.path.template=/getvinj/{uncode}
GETVINS.wrap.input.parameters=false
GETVINS.wrap.output.parameter=true
GETVINS.http.request.method=GET
GETVINS.consumes=*/*
GETVINS.produces=application/xml, application/json
GETVINS.response.code.parameter=
GETVINS.http.headers.parameter=
GETVINS.CODE.usage=input
GETVINS.CODE.pathparam=uncode
GETVINS.NOM.usage=output
GETVINS.DESVINS_LENGTH.usage=output
GETVINS.DESVINS.usage=output
 IWS00102I - Command completed successfully.
$
getWebServiceProperties.sh  -server WFORMATION -service GETVINJ

Name:                        GETVINJ                                      
Description: GETVINJ
Startup type: Automatic
Type: REST
Status: Stopped
Runtime user ID: CM
Install path: /www/WFORMATION/webservices/services/GETVINJ Program object path: /QSYS.LIB/FORMATIONX.LIB/GETVINS.PGM
PCML file path: /www/WFORMATION/webservices/services/GETVINJ/GETVINJ.pcml
Library list: FORMATIONX;BDVIN1;
Library list position: *LAST
Connection pool properties
Default CCSID: *USERID
Use maintenance threads: true
Cleanup interval (seconds): 300
Maximum number of connections: *NOMAX
Maximum inactivity time (seconds): 3600
Maximum life time (seconds): 86400
Maximum use count: *NOMAX
Maximum use time (seconds): *NOMAX
Transport metadata:
Transport headers:
stopWebservicesServer.sh -server WFORMATION 
IWS00102I - Command completed successfully. 
$ 

startWebservicesServer.sh -server WFORMATION 
IWS00102I - Command completed successfully. 
$ 
saveWebServices.sh -server WFORMATION -saveFile /QSYS.LIB/LIBSAVF.LIB/WFORMATION.FILE
                   -serviceList *ALL 
IWS00102I - Command completed successfully. 
$ 

-> le fichier WFORMATION (SAVF) a bien été créé dans LIBSAVF

 

pour passer la commande directement depuis un CL

QSH CMD('/qibm/proddata/os/webservices/V1/server/bin/<votrescript.sh> paramètre1 paramètre2 ...')



en 2010, un groupe PTF permet de déléguer des droits via l'administration graphique.

Sans cette délégation, il faut être *ALLOBJ et *IOSYSCFG pour administrer les serveurs mais aussi déployer les web services.

Ajoutons un utilisateur à la liste des utilisateurs autorisés :

Il faut ensuite spécifier les droits (globalement par type de serveur ou serveur par serveur)


Enfin, les Groupes PTF de 2012 (SF99115 level 23 et SF99368 level 11) améliorent le serveur de Web service pour le faire utiliser Axis 1.5 (à la place de 1.3)

(voyez ce Wiki pour accéder aux dernières nouveautés)

Une fois installées ces correctifs vous verrez apparaître une option de migration

Vous aurez aussi d'autres options :

la V7R2 amenant une nouvelle version de PCML (6) supportant les dates, les restrictions concernant les paramètres s'estompent


• Déclarons un programme ayant des dates en paramètre

le service est déployé , testons

Il faut saisir la date au format xs:dateTime du XML, soit

sans paramétrage particulier, vous recevez une erreur IWAB0383E

avec la propriété Java com.ibm.xml.xlxp.jaxb.opti.level=0, cela fonctionne

Serveur/properties/JVM options -> bouton EDIT



Enfin, le Groupes PTF de 2014 (SF99713 level 5) propose des nouveautés importantes :


pour les nouveaux services, les variables date sont mieux gérées et doivent être saisie au format xs:date


Résultat ->


le format xs:dateTime est toujours vrai pour les timestamp

l'option java com.ibm.xml.xlxp.jaxb.opti.level=0 est toujours obligatoire à ce jour.


•Autres nouveautés (intégrées également dans SF99368 level 31, de la version 7.1) :

Les paramètres sont reconnus grâce à PCML, indiquez le sens d'utilisation


Ensuite précisez




Avec SI56883, vous pouvez préciser plusieurs types de média en retour et le type est libre (text/html, par ex.)


Il faut alors fixer la variable d'environnement CONTENT_TYPE pour préciser dynamiquement le type retourné à chaque appel.

Ensuite, Indiquez le profil utilisateur à utiliser

la liste de bibliothèques

les informations d'entête HTTP à transporter, puis vous arrivez sur l'écran final







Quelques tests

*PATH_PARAM (celui déployé ci-dessous)



*QUERY_PARAM

nouveau service

paramètre en entrée



Format de sortie JSON


Format de sortie HTML

Exemple

Avec les tables résultat de CALL CREATE_SQL_SAMPLE




Résultat


Deuxième Exemple avec une image

Nous utiliserons ici

  1. une "ruse" du Html permettant de charger une image inline avec <img src="data:image/gif,base64, chaine-image-enBase64">
     En effet le serveur de web service, ne sait pas, à aujourd'hui retourner de la donnée binaire

  2. l'API de Sott KLEMENT pour convertir en base64
Exemple

Avec les tables résultat de CALL CREATE_SQL_SAMPLE


 

Cette fois nous renseignerons

  1. le code status obligatoirement sans une variable intéger 10 chiffres
  2. l'entête HTTP obligatoirement dans un tableau







Ces nouveaux Web services peuvent être testés par les dernières versions de SoapUI


Indiquez l'URL


SoapUI va découvrir les paramètres (ici {prod}) , assignez des valeurs et exécutez :




ou par POSTMAN, plus simple pour tester les web servies REST

ici la création d'un client par POST



Un seul paramètre peut être indiqué Input Source *NONE


Le pgm attend une DS ClientRecu composée des champs indiqués en haut (id, reference,...)

Il retourne plusieurs paramètres :

Les champs sont transmis en minuscules, car le programme utilise PGMINFO(*PCML:*MODULE : *DCLCASE)



En V6R1, et de manière intégrée au système d'exploitation, nous avons des mécanismes pour accéder à un service web depuis le langage RPG (entre autre).

on dit "consommer un service web"...

 

L'exemple (SOAP) fourni par IBM est basé sur le service ConvertTemp automatiquement créé lors de la création du serveur d'application

 

Allez sur la page

 

Si vous le testez, vous voyez (remarquez le paramètre param0, qui est une structure composée d'une seule zone _TEMPIN) :

 

Utilisation en PHP


Débug :

<?php

  $client = new SoapClient("http://AS400S:10030/web/services/ConvertTemp?wsdl");

  $param = array("param0" => array("_TEMPIN" => 100));

  $result = $client->converttemp($param);


  echo "<br>resultat ";

   var_dump($result);


?>

Résultat :

>

Pour accéder à la valeur

<?php

  $client = new SoapClient("http://AS400S:10030/web/services/ConvertTemp?wsdl");

  $param = array("param0" => array("_TEMPIN" => 100));

  $result = $client->converttemp($param);

  echo $result->return->_TEMPOUT;

?>



ATTENTION, sur les dernières versions du serveur de web services les noms des variables ne sont plus précédés de "_", sauf à indiquer enabled ici :




(en cas de doute, vérifiez le fichier .wsdl)

 

Si vous retournez un DS à occurrences -> DIM(xx) , tenez en compte dans votre code

Démo avec notre exemple retournant une DS INFOCENTRE à NBPROD occurrences



affiche



en .NET

Après avoir ajouté une référence web dont l'URL est "http://AS400:10030/web/services/ConvertTemp?wsdl"

Dim wsTemp As New AS400_Temperature.ConvertTemp
Dim input As New AS400_Temperature.CONVERTTEMPInput

   input._TEMPIN = txtF.text
   Response.Write(wsTemp.converttemp_XML(input))

Par exemple sur l'événement "click" d'un bouton cmdOK (ici libellé Go)

Protected Sub cmdOK_Click(ByVal sender As Object, ByVal e As System.EventArgs)
 Dim wsTemp As New AS400_Temperature.ConvertTemp
 Dim input As New AS400_Temperature.CONVERTTEMPInput

  If txtF.text <> "" Then
     input._TEMPIN = txtF.text
     lblC.Text = wsTemp.converttemp_XML(input)
  End If
End Sub

Résultat

 

Enfin, en RPG :

1/ récupérez le fichier de définition (WSDL) en cliquant sur View definition
(sinon, pour celui-ci uniquement , il est déja présent dans /QIBM/ProdData/OS/WebServices/V1/client)

2 / il faut générer le "stub" soit le programme "proxy" faisant l'interface entre Axis et notre RPG.

pour cela nous utiliserons l'outil WSDL2WS.SH sous shell (STRQSH)

> /QIBM/ProdData/OS/WebServices/V1/Client/bin/wsdl2ws.sh
   -lc -o/myconverttemp
   /QIBM/ProdData/OS/WebServices/V1/Client/samples/ConvertTemp/ConvertTemp.wsdl


   Code generation completed.
$

Si vous passez la commande ls, vous devez voir :

$
> ls /myconverttemp
ConvertTempPortType.c CONVERTTEMPInput.c CONVERTTEMPResult.c
ConvertTempPortType.h CONVERTTEMPInput.h CONVERTTEMPResult.h
$

L'utilisation de ces sources C en RPG était assez complexe.


En début 2011 les PTF suivantes SI42234 (V5R40) , SI42236 (V6R10) , SI42235 (v7) apportent un nouvel utilitaire wsdl2rpg

comme le précédent, il se trouve dans QIBM/ProdData/OS/WebServices/V1/Client/bin/

> /QIBM/ProdData/OS/WebServices/V1/Client/bin/wsdl2rpg.sh
   -o/myconvertRPG
   -s/QSYS.LIB/MABIB.LIB/CONVERT.SRVPGM
   http://as400:10041/web/services/ConvertTempService/ConvertTemp?wsdl


Code generation completed. Generated files in directory
'/myconvertRPG'.

Attempting to create service program...

Service program created. Service program is
'/QSYS.LIB/MABIB.LIB/CONVERT.SRVPGM'.

$

L'option -s demande la création d'un programme de service dans la bibliothèque MABIB.

Cette fois il génère aussi du RPG, soit 6 fichiers en plus, portant le nom du type de port (ConvertTempPortType dans nos exemples)

ConvertTempPortType.CL le CL de création du *SRVPGM
ConvertTempPortType.rpgleinc fichier à inclure (dans votre programme)
ConvertTempPortType.rpgle le code d'invoquation du web service (*SRVPGM)
ConvertTempPortType_util.rpgleinc fichier à inclure (dans le source suivant)
ConvertTempPortType_util.rpgle divers utilitaires
ConvertTempPortType__xsdtypes.rpgleinc différents types de données standards

le fichier de référence est le xxxxxxxx.PortType.rpgleinc (ConvertTempPortType.rpgleinc)
où xxxxxxxx représentant le nom du service tel que défini dans "http://AS400S:10030/web/services/xxxxxxxx?wsdl"

qui contient les définitions dont vous avez besoin, et qu'il faut inclure dans votre code :

Attention.

avec le niveau 9 de SF99713 (printemps 2015) les fonctions sont renommées :

cette version introduit un bug (en Français) : un paramètre est nommé #_return
-> voir l'APAR SE62384

La PTF SI57437 corrige cela.

 

Pour faire en RPG l'équivalent du test suivant (sur ConvertTemp) :


Ecrivez



Remarquez dans la fenêtre structure, le prototype pour l'opération (suite au /copy)



les paramètres sont en fait des DS possédant deux sous-zones significatives

Par exemple celui-ci attend des paramètres plus simples (voyez le .wsdl)



ce qui génère (par wsdl2rpg)


le code suivant est cohérent (et fonctionne) :

 


Enfin, si vous devez travailler en HTTPS (avec un certificat)

 

  1. Allez chercher le certificat sur le serveur concerné
  2. Installez le certificat sous Digital Certificat Manager
  3. Dans votre code, utilisez l'API axiscStubSetSecure(), après avoir ajouté le /copy
  /copy /QIBM/ProdData/OS/WebServices/V1/client/include/Axis.rpgleinc
  * qui contient
 D* axiscStubSetSecure...
D* PR EXTPROC('axiscStubSetSecure')
D* pStub * Value
D* pKeyRingFile * Value OPTIONS(*STRING)
D* pKeyRingSorP * Value OPTIONS(*STRING : *NOPASS)
D* pKeyRingLabel * Value OPTIONS(*STRING : *NOPASS)
D* pV2Cipher * Value OPTIONS(*STRING : *NOPASS)
D* pV3Cipher * Value OPTIONS(*STRING : *NOPASS)
D* pTLSCipher * Value OPTIONS(*STRING : *NOPASS) /free if stub_create_convertTempServices; axiscStubSetSecure(WsStub.handle:
'/QIBM/USERDATA/ICSS/CERT/SERVER/DEFAULT.KDB': 'motdepasse': 'label':'NONE':'05':'NONE');

Si vous recevez HTTPTransportException: HTTPS transport error.GSKit Error is 428 - No certificate is available for SSL processing.
  ne mettez rien en 3eme paramètre (remplacez motdepasse par une chaine vide)


Si vous avez des temps de réponses particulièrement longs, utilisez axiscStubSetTransportConnectTimeout(handle, nb-de-secondes)

Si vous rencontrez des problèmes, lancez une trace par axiscAxisStartTrace(’/tmp/axis.log’:*NULL) en début de pgm.

 

tous les détails sur ces API : http://www-03.ibm.com/systems/resources/systems_i_software_iws_pdf_WebServicesClient_new.pdf


 

Si vous préférez utiliser SQL, la PTF SF99701 level 23 propose une consommation de web services (Rest) via des fonctions dans SYSTOOLS

le but de ces fonctions est de consommer des services web plutôt orientés REST


Regardons à travers des exemples

Values SYSTOOLS.HTTPGETCLOB('http://www.volubis.fr' ,'') ;
  


Récupère dans une variable le contenu de notre page d'accueil




Pour les fonctions HTTPPOSTBLOB|CLOB et HTTPPUTBLOB|CLOB, il y un troisième paramètre: les données à transmettre
(avec GET les paramètres sont dans l'URL)

      

Pour une page web "classique" , par formulaire

->



Voyez cet extrait de code

(pensez simplement à indiquer le type de contenu formulaire : "x-www-form-urlencoded" pour simuler un formulaire)

-- Appel d'une page PHP (normalement un formulaire avec "prenom")
Values SYSTOOLS.HTTPPOSTCLOB('http://as400/php/exemples/tp/tp0.php' ,
         CAST ('<httpHeader>
                  <header name="Content-Type" value="application/x-www-form-urlencoded"/>
                </httpHeader>'  AS CLOB(1K)),
         CAST('prenom=christian'  AS CLOB(10K))  ) 

 


Si le site retourne du XML, l'utilisation de la fonction XMLTABLE permettra de ne recevoir que les données utiles

Ici, le site www.redbooks.ibm.com propose des flux RSS donnant les publications récentes


le code

SELECT *
 FROM XMLTABLE('$result/rss/channel/item' 
 PASSING XMLPARSE(
  DOCUMENT 
     SYSTOOLS.HTTPGETBLOB('http://www.redbooks.ibm.com/rss/iseries.xml','')
  ) as "result"
 COLUMNS 
  title VARCHAR(128) PATH 'title',
  description VARCHAR(1024) PATH 'description',
  link VARCHAR(255) PATH 'link',
  pubDate VARCHAR(20) PATH 'substring(pubDate, 1, 16)'
 ) AS RESULT; 
  

permet de lire les données de manière structurée :


Ce code permet de lire le fichier cours.xml , s'il est disponible via HTTP

SELECT cours, texte, motcle1,  monthname(modif) concat '-' concat year(modif)
 FROM XMLTABLE('$result/AF400/COURS' 
  PASSING XMLPARSE( 
  DOCUMENT 
   SYSTOOLS.HTTPGETBLOB('http://as400.volubis.intra/af4dir/courshtm/XML/cours.xml','')
 ) as "result"
 COLUMNS 
  cours CHAR(10) PATH 'AF4MBR',
  texte CHAR(50) PATH 'AF4TXT',
  motcle1 VARCHAR(20) PATH 'MOTCL1',
  MODIF DATE PATH 'DATOUT'
 ) AS TABLEXML;  

Ce code permet de le lire via HTTP, la page étant protégée par un mot de passe

-- LECTURE fichier XML via INTERNET avec authentification
SELECT cours, texte, motcle1, monthname(modif) concat '-' concat year(modif)
FROM XMLTABLE('$result/AF400/COURS'
PASSING XMLPARSE(
DOCUMENT
SYSTOOLS.HTTPGETBLOB('http://af400:motdepasse@as400.volubis.fr/af4dir/courshtm/XML/cours.xml','')
) as "result"
COLUMNS
cours CHAR(10) PATH 'AF4MBR',
texte CHAR(50) PATH 'AF4TXT',
motcle1 VARCHAR(20) PATH 'MOTCL1',
MODIF DATE PATH 'DATOUT'
) AS TABLEXML;

Sur la base d'un service ayant ce fichier WSDL : http://www.webservicex.net/stockquote.asmx?WSDL

et qui génère sous SoapUI cette enveloppe SOAP en entrée
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://www.webserviceX.NET/">
  <soapenv:Header/>
    <soapenv:Body>
       <web:GetQuote>
           <web:symbol>IBM</web:symbol>
      </web:GetQuote>
  </soapenv:Body>
</soapenv:Envelope>

Appel d'un Web service, utilisation du deuxième paramètre permettant de fournir les entêtes http,
le troisième contenant la demande(body), soit l'enveloppe SOAP. (ici, nous demandons la valeur de l'action IBM à aujourd'hui dans une enveloppe SOAP)

-- Appel d'un web service , récupération de l'enveloppe SOAP réponse dans une variable
VALUES SYSTOOLS.HTTPPOSTCLOB('http://www.webservicex.net//stockquote.asmx',
CAST ('<httpHeader>
<header name="Content-Type" value="text/xml;charset=utf-8"/>
<header name="SOAPAction" value="&quot;http://www.webserviceX.NET/GetQuote&quot;"/>
</httpHeader>' AS CLOB(1K)),
CAST('<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetQuote xmlns="http://www.webserviceX.NET/">
<symbol>IBM</symbol>
</GetQuote>
</soap:Body>
</soap:Envelope>' AS CLOB(10K)) ) ;
SoapUI nous montre l'enveloppe Soap en sortie, comme ceci :

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <soap:Body>
  <GetQuoteResponse xmlns="http://www.webserviceX.NET/">
   <GetQuoteResult>

    <![CDATA[
      <StockQuotes><Stock> <Symbol>IBM</Symbol><Last>182.46</Last><Date>11/14/2013</Date>
      <Time>12:08pm</Time><Change>-1.09</Change><Open>180.63</Open><High>182.90</High><Low>179.66</Low>
     <Volume>3474990</Volume><MktCap>198.1B</MktCap><PreviousClose>183.55</PreviousClose>
     <PercentageChange>-0.59%</PercentageChange><AnnRange>172.57 - 215.90</AnnRange><Earns>14.439</Earns>
    <P-E>12.71</P-E><Name>International Bus</Name></Stock></StockQuotes>
    ]]>

   </GetQuoteResult>
  </GetQuoteResponse>
 </soap:Body>
</soap:Envelope>

-- Appel d'un web service , récupération de l'enveloppe SOAP, "parsée"
-- il y a des espaces de nommage (xmlns) SOAP et d'autres propres au web service, d'où -> *:
-- les données extraites sont elle-même au format XML (on voit que l'action est à 210 $ 55 ) SELECT* FROM XMLTABLE('$result/*:Envelope/*:Body/*:GetQuoteResponse' PASSING XMLPARSE( DOCUMENT SYSTOOLS.HTTPPOSTCLOB('http://www.webservicex.net//stockquote.asmx', CAST ('<httpHeader> <header name="Content-Type" value="text/xml;charset=utf-8"/> <header name="SOAPAction" value="&quot;http://www.webserviceX.NET/GetQuote&quot;"/> </httpHeader>' AS CLOB(1K)), CAST('<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <GetQuote xmlns="http://www.webserviceX.NET/"> <symbol>IBM</symbol> </GetQuote> </soap:Body> </soap:Envelope>' AS CLOB(10K)) ) ) as "result" COLUMNS resultat VARCHAR(2000) PATH '*:GetQuoteResult' ) AS TABLEXML;
-- Appel d'un web service , récupération de l'enveloppe SOAP, "parsée" pour lire le montant de l'action
-- puis découpage du XML extrait (présent dans CDATA[ ] )  à nouveau par la fonction XMLTABLE
<![CDATA[
         <StockQuotes>
           <Stock><Symbol>IBM</Symbol><Last>182.46</Last><Date>11/14/2013</Date>
           <Time>12:08pm</Time><Change>-1.09</Change><Open>180.63</Open><High>182.90</High><Low>179.66</Low>
           <Volume>3474990</Volume><MktCap>198.1B</MktCap><PreviousClose>183.55</PreviousClose>
           <PercentageChange>-0.59%</PercentageChange><AnnRange>172.57 - 215.90</AnnRange><Earns>14.439</Earns>
          <P-E>12.71</P-E><Name>International Bus</Name></Stock>
</StockQuotes> ]]>
WITH temp as (SELECT resultat
     FROM XMLTABLE('$result/*:Envelope/*:Body/*:GetQuoteResponse'
     PASSING XMLPARSE(
     DOCUMENT 
     SYSTOOLS.HTTPPOSTCLOB('http://www.webservicex.net//stockquote.asmx',
     CAST ('<httpHeader>
           <header name="Content-Type" value="text/xml;charset=utf-8"/>
           <header name="SOAPAction" value="&quot;http://www.webserviceX.NET/GetQuote&quot;"/>
           </httpHeader>' AS CLOB(1K)),
     CAST('<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
            <soap:Body>
              <GetQuote xmlns="http://www.webserviceX.NET/">
               <symbol>IBM</symbol> 
              </GetQuote>
            </soap:Body>
           </soap:Envelope>' AS CLOB(10K))  ) 
     ) as "result"
     COLUMNS 
      resultat  VARCHAR(2000) PATH '*:GetQuoteResult'
     ) AS TABLEXML
   )
select * from temp, 
     XMLTABLE ('$c/StockQuotes/Stock' passing XMLPARSE(DOCUMENT RESULTAT) as "c"
     COLUMNS 
     symbol     CHAR(30)    PATH 'Symbol',
     prix 	dec(11, 2 )    PATH 'Last' 
) AS X ;



Pour un service web REST retournant du XML, c'est plus simple


conjuguons à nouveau HTTPGETBLOB et XMLTABLE

SELECT*
   FROM XMLTABLE('$result/lstprodResult/RETOUR'
         PASSING XMLPARSE(
         DOCUMENT 
         SYSTOOLS.HTTPGETBLOB('http://as400:10042/web/services/LSTPRODR/suivant/25', '')
         ) as "result"
COLUMNS 
           nom  VARCHAR(50) PATH 'PR_NOM' ,
           tel varchar(20) PATH 'PR_TEL',
           nbvins dec(3, 0) PATH'NBVIN',
           cepage  char(20) PATH 'CEPAGE' 
        ) AS TABLEXML; 

Si le service retourne du JSON


EN PHP




Résultat

AVANT TR11(7.2)/TR3(7.3), voyez l'utilitaire XML2JSON / JSON2XML

(http://www.mcpressonline.com/programming/techtip-json-and-xml-conversion-in-db2-for-i.html)



Depuis TR1 (7.3) TR5(7.2) une fonction JSON_TABLE est intégrée à SQL

    (le deuxième caractère, optionnel est un code page) 



httpGetBlobVerbose
httpGetClobVerbose
httpPutBlobVerbose
httpPutClobVerbose
httpPostBlobVerbose
httpPostClobVerbose
httpDeleteBlobVerbose
httpDeleteClobVerbose
httpBlobVerbose
httpClobVerbose
httpHeadVerbose


qui s'utilisent comme ça

with temp as (
       select responsemsg as msg , responsehttpheader as header 
from table( systools.httpgetclobverbose('http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml' ,'')) as E)
select code, monnaie, taux from temp CROSS JOIN 

xmltable('$r/httpHeader' passing xmlparse(DOCUMENT header) as "r" COLUMNS code char(25) PATH '@responseCode', message char(25) PATH 'responseMessage' ) as x CROSS JOIN
xmltable('$m/*:Envelope/*:Cube/*:Cube/*:Cube' passing xmlparse(DOCUMENT msg) as "m" COLUMNS monnaie char(3) PATH '@currency', taux dec(11, 7) PATH '@rate' ) as y


Vous pouvez mettre des options à java, en créant un fichier d'options

-Xmx Mémoire pour java (par exemple -Xmx2g -> 2 Go)
http.proxyHost Coordonnées du Proxy
http.proxyPort port du proxy (80 par défaut)
http.proxyUser Utilisateur pour proxy
http.proxyPassword Mot de passe pour proxy
http.nonProxyHosts Liste des serveurs pour lesquels ne pas utiliser le proxy
javax.net.ssl.trustStore emplacement du fichier contenant la liste des certificats des sites de confiance
javax.net.ssl.trustStorePassword mot de passe pour ouvrir le fichier trustStore
javax.net.ssl.keyStore emplacement du fichier contenant la liste des certificats et des clés privées (keystore)
javax.net.ssl.keyStorePassword mot de passe pour ouvrir le fichier keyStore

pour ces dernières options, concernant l'accès HTTPS, voyez notre cours SSL


Le fichier de propriétés java peut être indiqué comme suit :

  1. en créant une variable d'environnement indiquant sa localisation
    ADDENVVAR ENVVAR(QIBM_JAVA_PROPERTIES_FILE)
    VALUE(/QIBM/userdata/java400/mySystem.properties)
  2. en créant un fichier SystemDefault.properties dans la home directory de l'utilisateur
  3. en créant un fichier SystemDefault.properties dans /QIBM/userdata/java400/


    voir https://www-304.ibm.com/partnerworld/wps/servlet/ContentHandler/stg_ast_sys_wp_access_web_service_db2_i_udf

 Attention

Toutes les fonctions HTTPxxxx sont livrées dans SYSTOOLS, donc "as is' et ne peuvent faire l'objet d'une demande de support



A noter, en Mai 2016, IBM propose aussi d'utiliser directement les API Axis pour consommer des web services REST (alternative aux fonctions httpgetblob/clob)

il vous faut :

7.3: SI60805, SI60808
7.2: SI60806, SI60809
7.1: SI60807, SI60810

Avec,

7.3 : SI63730, SI63759
7.2 : SI63729, SI63760
7.1 : SI63727, SI63761

Exemple général

**free
Ctl-Opt ;
// pour compiler CRTRPGMOD puis CRTPGM BNDSRVPGM((QSYSDIR/QAXIS10CC))
//puis
// ADDLNK OBJ('/qsys.lib/af4test.lib/datanantes.pgm') NEWLNK('/home/CM/datanantes')
// pour tester, sous QSH-> datanantes xml
// ********************************************************************
dcl-pi *N;
   wformat char(4);
 END-PI;
dcl-s format char(4);
/COPY /QIBM/ProdData/OS/WebServices/V1/client/include/Axis.rpgleinc
DCL-S rc         INT(10);
DCL-S tHandle    POINTER;
DCL-S uri        CHAR(200);
DCL-S response   CHAR(32768);
DCL-S request    CHAR(32768);
DCL-S propBuf    CHAR(100);
// --------------------------------------------------------------------
//  Web service REST : liste des aires de co-voiturage à Nantes.
// --------------------------------------------------------------------

// choix du format (xml,json,csv, ...) if %parms() <1; format = 'xml'; else; format = wformat; ENDIF;
// récupération des data (liste des aires de co-voiturage à Nantes) uri = 'http://data.nantes.fr/api/publication/24440040400129_NM_NM_00003/LOC_AIRES_COV_NM_STBL/conten PRINT ('==Retourne les aires de co-voiturage sur '); PRINT (uri); // Connexion. tHandle = axiscTransportCreate(uri:AXISC_PROTOCOL_HTTP11); if (tHandle = *NULL); PRINT ('TransportCreate() failed'); return; endif;
// méthode GET propBuf = 'GET' + X'00'; axiscTransportSetProperty(tHandle: AXISC_PROPERTY_HTTP_METHOD: %addr(propBuf)); // Exécution flushAndReceiveData();
// Déconnexion.
axiscTransportDestroy(tHandle);
*INLR=*ON;
       
// code des procédures extrait de developerWorks
// ============================================= // Print to standard out (under QSH) // ============================================= DCL-PROC PRINT ; dcl-pi *n; msg varchar(32767) const; end-pi;
  dcl-pr printf extproc(*dclcase);
     template pointer value options(*string);
     dummy int(10) value options(*nopass);
  end-pr;
  dcl-c NEWLINE CONST(x'15');
  printf(%TRIM(msg) + NEWLINE);
END-PROC;
// =========================================
// Handle error
// =========================================
DCL-PROC checkError ;
    dcl-pi *n;
       msg varchar(5000) const;
    end-pi;
 DCL-S axisCode   INT(10);
 DCL-S statusCode POINTER;
 DCL-S rc         INT(10);
 axisCode = axiscTransportGetLastErrorCode(tHandle);
 PRINT (msg + ' call failed: ' +
        %CHAR(axisCode) + ':' +
        %STR(axiscTransportGetLastError(tHandle)));
 if (axisCode = EXC_TRANSPORT_HTTP_EXCEPTION);
         rc = axiscTransportGetProperty(tHandle:
         AXISC_PROPERTY_HTTP_STATUS_CODE: %ADDR(statusCode));
         PRINT ('HTTP Status code: ' + %STR(statusCode));
 endif;
END-PROC;
// =========================================
// Flush and Receive data
// =========================================
DCL-PROC flushAndReceiveData;
    dcl-pi *n;
    end-pi;
 DCL-S header     POINTER;
 DCL-S property   CHAR(100);
 DCL-S bytesRead  INT(10) inz(0);
 clear response;
 clear header;
 // Flush data so request is sent
 rc = axiscTransportFlush(tHandle);
 if (rc = -1);
      checkError ('TransportFlush()');
       return;
 endif;
 // Receive data and print out data and response to stdout
 rc = axiscTransportReceive(tHandle: %ADDR(response): %SIZE(response): 0);
 if (rc = 0);
     PRINT ('No data to read');
 else;
     dow rc > 0 AND bytesRead < %SIZE(response);
         bytesRead = bytesRead + rc;
         rc = axiscTransportReceive(tHandle:
         %ADDR(response)+bytesRead:
         %SIZE(response)-bytesRead:
         0);
     enddo;
 endif;
 if (rc = -1);
     checkError ('TransportReceive()');
 elseif (bytesRead  > 0);
         PRINT ('Bytes read: ' + %CHAR(bytesRead));
         PRINT ('Data: ' + response);
 endif;
 if (rc > -1);
     rc = axiscTransportGetProperty(tHandle:
           AXISC_PROPERTY_HTTP_STATUS_CODE:
          %addr(header));
     if (rc = -1);
         checkError ('TransportGetProperty()');
         else;
     PRINT ('HTTP status code: ' + %str(header));
     endif;
 endif;
END-PROC;

Testons

 



Voyez nos différentes solutions pour lire du JSON : http://www.volubis.fr/freeware/READJSON.html


Copyright © 2016 VOLUBIS