Vous êtes sur la page 1sur 102

1 - Principe des Web Services

Le but fondamental d'un Service Web est de rcuprer sur un poste le rsultat d'un
calcul effectu sur un autre poste.
Voici quelques exemples simples:
consultation de la mto
consultation et rservation de trains / avions / htels
rcupration de cotations boursires et calculs sur ces valeurs
Pour des documents plus lourds:
traduction de textes (BabelFish)
tlchargement de carte en fournissant des coordonnes (longitude, lattitude)
ou une adresse postale (Mappoint)
consultation de produits Amazon
Outre le dialogue en utilisant ou non un explorateur internet, les services web
permettent aussi d'effectuer des traitement distants entre deux application
("Business to Business, ou encore B2B). Par exemple:
un libraire (un point de vente quelconque, une usine) vend (consomme) des
produits. Lorsque le stock attend un certain seuil, il faut rapprovisionner. La
faon traditionnelle est de tlphoner ou d'envoyer un fax. Un peu mieux,
utiliser Internet. Et, naturellement, sans aucune intervention humaine,
un Service Web. Par exemple le logiciel de dstockage contient les seuils de
rapprovisionnement, et ds que celui ci est franchi, une commande est
envoye automatiquement
tout systme de surveillance (un laminoir)

2 - Construction d'un Service Web


2.1 - Fonctionnement des Web Services

Nous allons illustrer ceci sur un exemple trs simple de conversion montaire:
conversion de Dollars en Euros (et rciproquement ventuellement).
Pour effectuer ce calcul:
l'utilisateur lance une application de conversion qui contient l'adresse
du Service Web de conversion, tape une valeur en Dollars et clique sur un
bouton
le Service Web reoit la demande, calcule le rsultat, et renvoit la rponse
l'utilisateur voit le rsulat de la conversion

Ainsi prsent, ce type de traitement est clairement du type Client / Serveur. Voici
le droulement du dialogue
le Serveur est lanc. Il contient

un excutable (.EXE ou .DLL) contenant les FUNCTIONs et


des PROCEDUREs que des Clients peuvent invoquer. Dans notre cas une
fonction qui convertit des Dollars en Euros:
Function f_euro(p_dollar: Integer): Integer

un Serveur TCP/IP capable de recevoir une demande Client, lancer l'excutable,


et renvoyer la rponse

Voici notre Serveur l'coute des Clients:

un client lance un .EXE qui contient un Service Web Client. Il demande la conversion
d'un montant en Dollar:

le Serveur TCP/IP reoit la requte, et charge l'excutable qui fait la conversion :

the TCP/IP Server renvoie la rponse:

Vous aurez remarqu que ce type d'change est trs similaire aux changes CGI ou
ISAPI, la diffrence que le Client n'a pas besoin d'tre un exlorateur Web, et la
requte n'est pas provoque par un clic sur un bouton <INPUT situ dans une

balise <FORM d'une page .HMTL. Le Client est contenu dans une
applicationWindows normale (un .EXE).

2.2 - Les Composants Ncesaires


Un service Web a besoin pour fonctionner
d'un rseau TCP/IP
d'un serveur HTTP capable de comprendre le protocole des Services Web
d'une extension du serveur, CGI ou ISAPI, qui contient le traitement effectu
par le service
de clients qui vont interroger le Service pour obtenir des rsultats

2.3 - Le Rseau TCP/IP


Celui-ci est disponible par dfaut sur la plupart des OS, dont Windows XP et
similaires

2.4 - Le protocole SOAP


2.4.1 - SOAP= HTTP + XML
L'excution depuis un poste d'un traitement sur un autre fait partie des techniques
RPC (Remote Procedure Call). Parmi les moyens pour mettre en oeuvre des RPC,
citons
CORBA (Common Object Request Broker Architecture, qui est trs
complet, mais trs vaste et trs complexe
DCOM (Distributed Component Object Model) qui est l'extension
"distribue" de COM. Solution purement Microsoft, et peu peu tomb en
dsutude cause de sa difficult de mise en oeuvre
et finalement les Services Web utilisant le protocole SOAP
(Simple Object Access Protocol

SOAP spcifie simplement comment se droulera le dialogue entre le Serveur et


les Clients. Devant le refus de DCOM, Microsoft a sous-trait Don BOX la
cration d'un nouveau protocole, et le rsultat a t SOAP.
SOAP se veut une simplification par rapport DCOM. Aussi, Don BOX a choisi:
d'utiliser le rseau TCP/IP
le protocole HTTP
un contenu au format .XML
Donc:
TCP/IP pour son ubiquit
HTTP car c'est le protocole utilis par le Web (serveurs et explorateurs Web).
Donc universellement accept, et utilisant sur les PC le port 80, qui est
pratiquement toujours laiss libre par les pare feu (sinon vous n'avez pas
accs au Web, ce qui de nos jours est terrible)
pour poser la question, une simple requte au format CGI aurait en gnral
suffi. Mais la rponse peut tre complex (une Table complte, par exemple),
il fallait trouver un format adquat. XML (eXtended Markup Language) a
t retenu.

2.4.2 - Le Format SOAP


Rassurez-vous, vous n'aurez ni construire les requtes au format HTTP, ni a grer
ce protocole, ni crire ou lire du .XML. Les couches Delphi se chargeront de tout.
Nanmoins prsentons rapidement le contenu des donnes changes:
lorsque le Client envoie sa requte, le Client envois au Serveur un texte compos d'une
en-tte HTTP et d'un contenu avec la fonction appele et ses paramtres au format
.XML:
POST /p_server_euro.co_euro/soap/i_server_euro HTTP/1.1
SOAPAction: "urn:u_i_server_euro-i_server_euro#f_euro"
Content-Type: text/xml
User-Agent: Borland SOAP 1.2
Host: localhost

Content-Length: 525
Connection: Keep-Alive
Cache-Control: no-cache
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAPENV="http://schemas.xmlsoap.org/soap/envelope/"
// -- ...ooo... Ed: truncated >
<SOAP-ENV:Body SOAP-ENV:encodingStyle=
"http://schemas.xmlsoap.org/soap/encoding/">
<NS1:compute_euro xmlns:NS1="urn:u_i_server_euroi_server_euro">
<p_euro xsi:type="xsd:int">30</p_euro>
</NS1:compute_euro>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
o:

p_server_euro est le nom de notre excutable (p_server_euro.exe)

i_server_euro est le nom de l'INTERFACE Delphi qui dfinit les mthodes


disponibles

f_euro est la fonction appele

30 est le montant en Dollars

le Serveur retourne la rponse avec le message suivant:


HTTP/1.1 200 OK
Content-Type: text/xml
Content-Length: 465
Content:
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAPENV="http://schemas.xmlsoap.org/soap/envelope/"
// -- ...ooo... Ed: truncated >
<SOAP-ENV:Body SOAP-ENV:encodingStyle=
"http://schemas.xmlsoap.org/soap/encoding/">
<NS1:f_euroResponse xmlns:NS1="urn:u_i_server_euroi_server_euro">
<return xsi:type="xsd:int">15</return>
</NS1:f_euroResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

o 15 est le rsultat en Euros

Les messages comportent donc


l'en-tte HTTP classique
une "enveloppe" au format .XML qui contient soit la requte soit la rponse

2.5 - WSDL
Nous allons crire un Serveur de Services Web et un Client de Services Web.
Si nous souhaitions consulter les livres Amazon ou rserver un train,
le Serveur de Service Web est dj crit (par la SNCF). Il nous suffit de crer
le Client WebService.
Il faut toutefois connatre :
le nom des mthodes offertes par le service SNCF
le nombre et le type des paramtres de chaque mthode
Ces informations sont disponibles dans un document .WSDL
(Web Service Definition Language, prononc "ouicedelle"). Voici le .WSDL de
notre requte
<?xml version="1.0" encoding="utf-8"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
// -- ...ooo... Ed: truncated
name="i_server_euroservice"
// -- ...ooo... Ed: truncated >
<message name="f_euro0Request">
<part name="p_euro" type="xs:int"/>
</message>
<message name="f_euro0Response">
<part name="return" type="xs:int"/>
</message>
<message name="compute_euro1Request">
<part name="p_euro" type="xs:int"/>

</message>
<message name="compute_euro1Response"/>
// -- ...ooo... Ed: truncated

Vous distinguerez dans ce document (tronqu: il fait environ 50 lignes) les noms du
service, de notre fonction f_euro et le paramtre p_euro et son type Int. Comme
nous n'aurons pas crire nous-mme ce type de donnes, nous ne nous attarderons
pas sur son contenu.
Le point important est que, pour crire un Client Web Service, nous pouvons
interroger le Serveur Web Service pour lui demander quels service il offre.
Une fois ce document rcupr, nous pouvons crer le Client.
Delphi va mme plus loin en offrant un Wizard qui partir du document .WSDL va
gnrer un UNIT qui importer l'INTERFACE permettant de dialoguer avec
leServeur. Nous utiliserons ce Wizard d'importation .WSDL ci-dessous.

2.6 - Le Serveur HTTP


Il suffit que le poste serveur dispose d'un Serveur HTTP. Parmi les possibilits,
citons
sur Windows 95 et Windows 98, PWS (Personal Web Server)
sur XP Home, aucun serveur n'est fourni par Microsoft. Nous pouvons
toujours acheter er installer IIS (Internet Information Server) ou Appache.
Nous pouvons utiliser des solutions purement Delphi:
o depuis Delphi 6, le WAD (Web Application Debugger)
o divers serveurs construits partir des composants TCP/IP Indy
o nous avons nous-mmes utilis un serveur HTTP CGI construit
partir de nos sockets, et adapt pour les Services Web
pour XP Pro, nous disposons d'une version IIS light

Dans cet article, nous utiliserons les Serveur HTTP de dbugging intgr Delphi,
WAD.

2.7 - Type d'excutable Service Web


L'excutable qui contiendra nos mthodes et qui sera lanc par le Serveur TCP/IP
peut avoir trois formats:
une .DLL ISAPI (Internet Server API) ou NSAPI (NetScape API
une extension serveur CGI (Common Gateway Interface)
une application incluant le Serveur HTTP et une extension CGI
Les deux premires solutions ncessitent l'existence d'un Serveur HTTP (IIS,
Appache ou autre), alors que la troisime emploie le Serveur Indy de mise au point
intgr Delphi, WAD.
Nous utiliserons WAD, qui est fourni avec Delphi

3 - Le Serveur Web Service


3.1 - Construction de l'extension Serveur
3.1.1 - Mise en place en utilisant le Wizard
Voici les tapes pour crer notre service de conversion:
dmarrez Delphi 2006 Windows
slectionnez le Wizzard qui cre une application de Service Web en slectionnant "File |
New | Other | Delphi Project | Web Services | Soap Server"

le Wizard nous propose 3 solutions:

Ces possibilits correspondent:

une .DLL ISAPI (Internet Server API) ou NSAPI (NetScape API

une extension serveur CGI (Common Gateway Interface)

une application incluant le Serveur HTTP et une extension CGI

Comme indiqu ci-dessus, nous utiliserons la troisime solution qui fonctionne avec
le Serveur HTTP Indy fourni avec Delphi et qui en plus permet de dbugger
notre Serveur

slectionnez "Web App Debugger" (alias WAD)


la boite d'dition devient accessible
tapez le nom d'une co-classe. Nous suggrons co_euro_dollar, puis tapez "ok"
Delphi nous demande si nous souhaitons crer automatiquement l'INTERFACE et
l'IMPLEMENTATION de noter Service:

tapez "Yes"
Delphi nous demande le nom de notre Service
nous suggrons d'appeler l'INTERFACE, et par consquent vous pouvez taper

dans "Service Name, "_server_euro_dollar" (le I sera ajout automatiquement)

dans "Unit Identifier" "u_i_server_euro_dollar"

Supprimez aussi "Generate Comments" pour allger le texte

Puis cliquez "Ok"


Delphi gnre les fichiers ncessaires au Serveur de Services Web

Ces fichiers comprennent:


le projet (.DPR)
l'UNIT qui contient l'INTERFACE, appele actuellement
U_I_SERVER_EURO_DOLLARINTF
l'UNIT qui contient la CLASSe qui implmentera not INTERFACE,
appele U_I_SERVER_EURO_DOLLARIMPL
un tWebModule permettant de rceptionner les requtes
du Client Web Service, appel actuellement UNIT2
une tForm, qui actuellement ne contient aucun composant, mais qui sera
affiche chaque fois que le Serveur est activ

Nous suggrons de renommer les diffrentes parties de la faon suivante:


slectionnez "Project Manager | PROJECT1.EXE | click droit | Rename" et tapez
P_SERVER_EURO_DOLLAR_WAD
renommez U_I_SERVER_EURO_DOLLARINTF U_I_SERVER_EURO_DOLLAR
renommez U_I_SERVER_EURO_DOLLARIMPL U_C_SERVER_EURO_DOLLAR
renommez UNIT2 U_WM_SERVER_EURO_DOLLAR
renommez UNIT1 U_F_SERVER_EURO_DOLLAR

Cre un rpertoire SERVER_WAD, et slectionnez "File | Save All"

3.1.2 - Dfinition des mthodes du Service Web


Il faut prsent placer dans l'INTERFACE les mthodes que nous souhaitons
utiliser. Nous allons utiliser une FUNCTION pour passer du dollar l'euro, et
unePROCEDURE pour la conversion inverse.
Par consquent:
slectionnez "Project Manager | u_i_server_euro_dollar".
le code gnr par le wizzard contient les importations, une INTERFACE actuellement
vide, et une initialisation que enregistre notre INTERFACE
dans le code, ajoutez la dfinition de nos deux mthodes:
unit u_i_server_euro_dollar;
interface
uses InvokeRegistry, Types, XSBuiltIns;
type i_server_euro_dollar=
interface(IInvokable)
['{54C806EE-35DD-4F1C-8530-4B31F1DBFE3A}']
function f_dollar_to_euro(p_dollar: Double): Double; Std
Call;
procedure euro_to_dollar(p_euro: Double;
var pv_dollar: Double); StdCall;
end; // i_server_euro_dollar
implementation
initialization
InvRegistry.RegisterInterface(TypeInfo(i_server_euro_dollar));
end.

Notez que
notre INTERFACE i_server_euro_dollar descend de iInvokable. C'est ce
qui permettra de provoquer un appel depuis le Client Web Service
les mthodes DOIVENT avoir l'attribut STDCALL pour pouvoir tre
invoques
pour pouvoir retrouver cette INTERFACE parmi d'autres qui seraient
ventuellement disponibles sur le mme Serveur, Delphi a plac

automatiquement un GUID (Globally Unique Identifier). Nous pouvons


changer ce numro tant que nous le souhaitons en tapant Shift-Ctrl-G. La
valeur de cet identifiant unique a pour seul but de pouvoir identifier
notre INTERFACE. Ce GUID ne sera pas directement utilis par notre code
dans la suite (il ne sera pas invoqu par notre code, ou par le Client). De plus
ce GUID ne correspond pas un objet COM. C'est uniquement un moyen
d'identification unique.
InvRegistry.RegisterInterface permet de stocker les informations qui
permettront d'appeler la CLASSe qui implmente notre INTERFACE.
Notez que cet enregistrement est purement temporaire. Rien ne sera stock
ce niveau dans la base de registres (Registry). La table de correspondance
entre le nom "i_server_euro_dollar" et
l'INTERFACE i_server_euro_dollar est cre lorsque notre CGI Serveur est
charg

3.1.3 - L'implmentation de notre INTERFACE


Il nous faut prsent implmenter les mthodes que nous avons propos dans
notre INTERFACE.
Par consquent:
copiez les deux mthodes que nous avons places dans l'INTERFACE dans le pressepapier
slectionnez "Project Manager | u_c_server_euro_dollar".
le code gnr par le wizzard contient les importations, une CLASS actuellement vide et
une intialization que enregistre notre CLASSe
modifiez le nom de l'UNIT qui contient notre INTERFACE en
U_I_SERVER_EURO_DOLLAR
collez les deux dfinitions de mthode dans la CLASSe
tapez Shift-Ctrl-C pour gnrer les implmentations de nos deux mthodes
dans ces mthodes, tapez les calculs de conversion
Le rsultat est:
unit u_c_server_euro_dollar;
interface
uses InvokeRegistry, Types, XSBuiltIns
, u_i_server_euro_dollar;

type c_server_euro_dollar=
class(TInvokableClass, i_server_euro_dollar)
public
function f_dollar_to_euro(p_dollar: Double): Double; St
dCall;
procedure euro_to_dollar(p_euro: Double;
var pv_dollar: Double); StdCall;
end; // c_server_euro_dollar
implementation
// -- c_server_euro_dollar
function c_server_euro_dollar.f_dollar_to_euro(p_dollar: Doub
le): Double;
begin
Result:= p_dollar / 1.51;
end; // f_dollar_to_euro
procedure c_server_euro_dollar.euro_to_dollar(p_euro: Double
;
var pv_dollar: Double);
begin
pv_dollar:= p_euro* 1.51;
end; // euro_to_dollar
initialization
InvRegistry.RegisterInvokableClass(c_server_euro_dollar);
end.

Notez que:
notre CLASSe doit descendre de tInvokableClass, et de
notre INTERFACE i_server_euro_dollar
l'initialisation de notre UNIT
appelle InvRegistry.RegisterInvokableClass qui stocke dans notre base de
correspondance notre CLASSe

3.1.4 - Le WebModule
Nous n'avons pas besoin de modifier ce module, mais nous allons le prsenter
rapidement.
Le code est le suivant
unit u_wm_server_euro_dollar;
interface
uses SysUtils, Classes, HTTPApp, InvokeRegistry, WSDLIntf, TypInf
o,
WebServExp, WSDLBind, XMLSchema, WSDLPub, SOAPPasInv,
SOAPHTTPPasInv,
SOAPHTTPDisp, WebBrokerSOAP;
type TWebModule1=
class(TWebModule)
HTTPSoapDispatcher1: THTTPSoapDispatcher;
HTTPSoapPascalInvoker1: THTTPSoapPascalInvoker;
WSDLHTMLPublish1: TWSDLHTMLPublish;
procedure WebModule1DefaultHandlerAction(Sender: TObje
ct;
Request: TWebRequest; Response: TWebResponse; var Hand
led: Boolean);
private
public
end; // TWebModule2
var WebModule1: TWebModule1;
implementation
uses WebReq;
{$R *.dfm}
procedure TWebModule1.WebModule1DefaultHandlerAction(Sende
r: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: B
oolean);
begin
WSDLHTMLPublish1.ServiceInfo(Sender, Request, Response, Ha
ndled);
end; // WebModule1DefaultHandlerAction
initialization
WebRequestHandler.WebModuleClass := TWebModule1;
end.

et:
la mcanique est base sur les composant WebBroker. Ceux-ci comportent
un tWebModule, qui peut contenir zro ou plusieurs tWebActionItems.
Supposons que nous ayons cr ("click droit souris | Action Editor | Add etc)
trois actions "un/" "deux/" et "trois/", avec chacune un vnement OnAction.
Si un client demande
http://url_serveur/p_nom_serveur/deux"

alors deuxAction sera excut. Il y a en plus toute une mcanique qui permet
o de traiter ou non un action
o de terminer aprs le traitement d'une action (Handled True)
o de laisser WebBroker excuter les actions qui suivent dans la liste
o de crer une action par dfaut qui sera excute si aucune action
prcdente n'a termin le traitement
Les noms "un", "deux" et "trois" de notre exemple fictif sont
appels PathInfo
dans notre cas
o une action par dfaut ayant un PathInfo "soap/" a t automatiquement
cre dans chaque extension Serveur Web Service
o une autre action "wsdl/" a t cre, et c'est l'action par dfaut
le tWebModule contient
o HTTPSoapDispatcher1 qui est charg de rceptionner les
demandes Client Web Services, et les orienter
vers HTTPSoapPascalInvoker1, en fonction dePathInfo
o HTTPSoapPascalInvoker1 est charg de transformer le nom d'une
mthode (par exemple la chane "f_dollar_to_euro") en un appel de la
mthode correspondante (dans notre cas
la FUNCTION f_serveur_euro)

o WSDLHTMLPublish1 est charg de gnrer le fichier .WSDL qui


dcrit nos deux mthodes

En effet, le WebModule contient aussi une


procdure WebModule1DefaultHandlerAction. Cette vnement correspond
un ActionItem cr automatiquement par le Wizard. Nous pouvons le voir de la
faon suivante:
slectionnez la forme du web modules et effectuez un clic droit
le dialogue d'dition des actions est affich, avec l'action cre par dfaut:

L'action qui provoque les calculs (et pas la fourniture du fichier .WSDL) est
implicite (pas affiche dans l'diteur d'actions).

3.1.5 - La fentre principale


Comme nous avons cr une application WAD, elle a une fentre (ce ne serait pas
le cas pour les ISAPI ou CGI purs).

Nous n'avons pas besoin d'y toucher. En gnral nous la colorions un peu pour
pouvoir la visualiser plus facilement, et nous la positionnons droite de l'cran
(cration de tForm.OnCreate et initialisation de Left et Top) pour viter
que Delphi ne positionne la fentre de faon plutt alatoire.

3.2 - Compilation de notre Serveur Web


3.2.1 - Indy 9
Le Serveur HTTP intgr WAD est construit partir du Serveur HTTP Indy.
Pour Delphi 2006, il s'avre que ce serveur WAD a t compil avec Indy 9.
Lors de l'installation de Delphi 2006, il nous a t demand de choisir
entre Indy 9 et Indy 10. Si vous avez slectionn Indy 9, vous pouvez sauter ce
paragraphe.
Si vous avez slectionn Indy 10, il faut permettre au compilateur de retrouver
les .DCU correspondant Indy 9. Pour cela:
slectionnez C:\Program Files\Borland\BDS\4.0\lib\Indy9

et collez cette adresse dans le press papier


slectionnez "Project | Options | Directories | Search Path" et collez-y ce chemin

3.2.2 - Compilation du Serveur


Nous compilons notre projet
tapez F9
la scurit Windows se rappelle notre bon souvenir:

tapez "dbloquer"

Notez que:
il FAUT compiler au moins une fois. Le rsultat est de stocker une
correspondance entre notre excutable et notre
service u_i_server_euro_dollar-i_server_euro_dollar dans la base de
registre:

La connaissance de cette entre permette ventuellement de faire le mnage


(ce qui est aussi possible depuis le WAD)
c'est cette entre qui permet de lancer le CGI sous contrle
du Web App Debugger
lorsque nous avons autoris Windows lancer notre Serveur, cela correspond
une nouvelle entre dans "Dmarrer | Paramtres | Panneau de
Configuration | Pare feu Windows | Exception":

Une autre information utile pour faire le mnage de temps en temps.

3.3 - Cration du fichier WSDL


3.3.1 - Pourquoi crer le fichier .WSDL
Normalement, pour crer le Client, nous n'avons pas besoin de crer un fichier
physique correspondant: nous pouvons interroger le Serveur Web Service pour lui
demander ces donnes. Notre version de Delphi 2006 ne permet cependant pas de
rcuprer ces donnes par une URL. En revanche l'importation depuis un fichier sur
disque fonctionne.

Enregistrement de ServerInfo

Le Web App Debugger va nous prsenter une liste des CGI disponibles. En fait, il
s'agit d'un service Web qui nous fournit la liste de NOS services web (ce service
interroge simplement la base de registres !).
Si vous venez d'installer Delphi 2006 (ou que vous n'avez jamais cr
de Services Web), SERVERINFO.EXE n'est pas enregistr.
Pour l'enregistrer la premire fois:
slectionnez C:\Program Files\Borland\BDS\4.0\Bin

et lancez SERVERINFO.EXE

Notez que:
Cette opration n'aura plus besoin d'tre effectue
titre anectdotique, SERVERINFO figurait bien dans la liste des WAD de la
base de registre (image avant la prcdente)

3.3.2 - Lancement de notre Serveur Web Service


Lanons prsent notre CGI:

fermez l'excutable du Serveur


slectionnez "Tools | Web App Debugger"
la fentre du Web App Debugger est affiche:

cliquez "Start" pour prparer la liste des CGI disponibles


le lien "http://localhost:8081/ServerInfo.ServerInfo" est utilisable et comporte un
soulign
cliquez le soulign pour demander SERVERINFO de nous fournir la liste des Serveurs
ServerInfo retourne une page Web, avec une tListBox contenant les CGI disponibles,
dont notre Serveur (que nous avons slectionn):

slectionnez le Serveur que vous souhaitez lancer. Dans notre cas, c'est
p_server_euro_dollar_wad.co_euro_dollar, et cliquez "Go"
une nouvelle page Web est affiche avec les informations concernant notre Service Web:

Sur la dernire figure


trois fentre sont l'cran:
o la fentre d'information sur les Services Web disponibles actuellement,
soit 2 services
o la fentre de ServerInfo (A)
o la fentre de notre service en jaune (B)
sur la fentre d'information, nous avons une partie pour chaque service (1 et
2).
prenons notre service: en (3) il y a un hyper-lien qui permet de visualiser le
fichier WSDL

3.3.3 - Consultation et Sauvegarde du .WSDL


Pour visualiser le .WSDL:
cliquez sur le lien (3)
le texte du .WSDL est affich dans une fentre Web:

Sans rentrer dans le dtail des donnes .WSDL, vous pouvez reconnatre des information
sur les mthodes et les paramtres de ce qui est offert par notre service

Pour sauvegarder les donnes dans un fichier


slectionnez "Fichier | Enregistre Sous" et placez le fichier dans le mme rpertoire que
les autres fichiers (le nom i_server_euro_dollar.xml est propos par dfaut et nous le
conservons)

4 - Cration d'un Client Web Service


4.1 - Consommation du Service
4.1.1 - Invocation d'une mthode Serveur

Nous allons prsent crer un Client qui va appeler nos mthodes depuis une
application connecte notre Serveur par TCP/IP.
Nous pourrions construire manuellement et envoyer la requte SOAP au Serveur,
puis rcuprer la rponse et la dcortiquer. Cette solution est possible en utilisant
des Sockets TCP/IP. C'est en fait la solution que nous avions adopte pour notre
confrence SOAP et DataSnap la confrence Borland en 2001.
Delphi utilise une technique qui automatise ces changes sans que nous ayons
traiter les messages SOAP. Au niveau principe:
Delphi construit en mmoire un composant qui a les mmes mthodes que
la CLASSe du Serveur. Ce composant est appel un Proxy : il se comporte,
du point de vue du Client comme le vritable composant Serveur.
Voici la situation aprs le lancement du Serveur et du Client:

o nous avons reprsent


o l'INTERFACE i_server_euro sur le Serveur
o le Proxy i_client_euro sur le Client
l'application Client effectue sa demande en appelant une mthode du Proxy

le Proxy transforme cette requte en un message SOAP qui est envoy


au Serveur:

le Serveur calcule sa rponse, qui est retourne au Proxy, lequel retourne la


valeur l'appelant:

Le rsultat de cette mcanique est que le Client effectue ses appels


de FUNCTION ou de PROCEDURE "comme si" la CLASSe Serveur avait t
tlcharge sur son PC.
Le Proxy est mis en oeuvre en utilisant un
composant tHttpRIO (RIO= Remote Invocation Object)

4.1.2 - Les composants Delphi


Le composant tHttpRIO a pour objectif de nous retourner
une INTERFACE correspondant l'INTERFACE sur le Serveur.
Pour cela, il faut que le Client ait la dfinition Pascal de cette INTERFACE
Et pour importer la dfinition de l'INTERFACE, nous utilisons un Wizard qui:
lit un .WSDL (par son URL ou en lisant un fichier .XML)
construit l'UNIT contenant:
o l'INTERFACE de notre Service (ainsi que les autres dfinitions si
notre Service comporte des types ou CLASSes annexes particulires,
ce qui n'est pas le cas pour notre exemple)

o en bonus, une FUNCTION qui retourne automatiquement


cette INTERFACE
Il s'avre que nous ne pouvons invoquer le Wizard qu'aprs avoir cr une
application Client.
Nous allons donc
crer une application Client
crer l'aide du Wizard l'UNIT qui contiendra l'INTERFACE
complter, dans la tForm de notre client, quelques appels de notre Service

4.2 - Cration du Client


Il s'agit d'une application Delphi des plus classiques:
slectionnez "File | New | Vcl Application Form"
sauvegardez le projet. Nous suggrons de crer un rpertoire CLIENT\ et de nommer les
fichiers U_F_CLIENT_EURO_DOLLAR et P_CLIENT_EURO_DOLLAR
compilez pour vrifier que tout est en ordre

Notez que
vous pourriez aussi ajouter la nouvelle application au groupe par "Project
Manager | ProjectGroup1 | Add New Project etc

4.3 - Importation de l'INTERFACE par le Wizard


Maintenant nous pouvons utiliser le Wizard qui va transformer le .WSDL en
une CLASSe locale du Client:
slectionnant "File | New | Other | Delphi Project | Web Services | Wsdl Importer"

et cliquez "Ok"
le Wsdl Import Wizard est affich (Ed: tronqu en largeur)

dans "Location of WSDL File", tapez le chemin et le nom du fichier .XML. Dans notre
cas:
C:\programs\fr\web\web_service_tutorial\server_wad\i_server_euro_dollar.xml
(vous pouvez utiliser l'ellipse ... pour localiser ce fichier
cliquez sur "Finish"
l'unit est prsente (Ed: tronqu en largeur):

cliquez "Finish"
l'unit est copie dans notre projet Client, sous le nom de
I_SERVER_EURO_DOLLAR1
nous suggrons de renommer l'UNIT U_I_CLIENT_EURO_DOLLAR

L'unit d'importation comporte (en enlevant les commentaires):


unit u_i_client_euro_dollar1;
interface
uses InvokeRegistry, SOAPHTTPClient, Types, XSBuiltIns;
type i_server_euro_dollar =
interface(IInvokable)
'{091A6317-83B7-FB51-3FD6-4BD47A83325F}']
function f_dollar_to_euro(const p_dollar: Double): Double; st
dcall;
procedure euro_to_dollar(const p_euro: Double;
var pv_dollar: Double); stdcall;
end; // i_server_euro_dollar
function Geti_server_euro_dollar(UseWSDL: Boolean=System.Fals
e;
Addr: string=''; HTTPRIO: THTTPRIO = nil): i_server_euro_doll

ar;
implementation
function Geti_server_euro_dollar(UseWSDL: Boolean;
Addr: string; HTTPRIO: THTTPRIO): i_server_euro_dollar;
const
defWSDL = 'C:\programs\fr\web\web_service_tutorial\server_wa
d\'
+ 'i_server_euro_dollar.xml';
defURL = 'http://localhost:8081/'
+ 'p_serveur_euro_dolar_wad.co_euro_dollar/soap/'
+ 'i_server_euro_dollar';
defSvc = 'i_server_euro_dollarservice';
defPrt = 'i_server_euro_dollarPort';
var RIO: THTTPRIO;
begin
Result := nil;
if (Addr = '')
then begin
if UseWSDL
then Addr := defWSDL
else Addr := defURL;
end;
if HTTPRIO = nil
then RIO := THTTPRIO.Create(nil)
else RIO := HTTPRIO;
try
Result := (RIO as i_server_euro_dollar);
if UseWSDL
then begin
RIO.WSDLLocation := Addr;
RIO.Service := defSvc;
RIO.Port := defPrt;
end
else RIO.URL := Addr;
finally
if (Result = nil) and (HTTPRIO = nil)
then RIO.Free;
end; // try ... finally
end; // Geti_server_euro_dollar
initialization
InvRegistry.RegisterInterface(TypeInfo(i_server_euro_dollar),
'urn:u_i_server_euro_dollar-i_server_euro_dollar', 'utf-8');
InvRegistry.RegisterDefaultSOAPAction(TypeInfo(i_server_euro_

dollar),
'urn:u_i_server_euro_dollar-i_server_euro_dollar#
%operationName%');
end.

et:
nous retrouvons bien l'INTERFACE que nous avions ct Serveur
la fonction Geti_server_euro_dollar permet simplement de nous retourner
une INTERFACE, avec plusieurs possibilits
o si nous ne fournissons aucun paramtres, un tHttpRio local est cr, et
est retourn transtyp en i_server_euro_dollar
o si nous demandons un .WSDL, i_server_euro_dollar pourra tre
utilis pour rcuprer le .WSDL
o nous pouvons aussi placer un tHttpRio sur la tForm Client, et fournir
celui-ci comme paramtre aprs l'avoir initialis

Comme nous appellerons Geti_server_euro_dollar sans paramtres, nous pouvons


(c'est optionnel) simplifier cette fonction.
Voici les modifications (cosmtiques) que nous apportons:
changez le
nom i_server_euro_dollar en i_client_euro_dollar (l'INTERFACE correspo
nd un proxy)
supprimez les paramtres de Geti_server_euro_dollar
veillez ce que les chanes littrales du bas appellent bien
'i_server_euro_dollar': ce sont elles qui seront utilises pour appeler
le Serveur
Voici notre unit modifie:
unit u_i_client_euro_dollar;
interface

uses InvokeRegistry, SOAPHTTPClient, Types, XSBuiltIns;


type i_client_euro_dollar =
interface(IInvokable)
['{091A6317-83B7-FB51-3FD6-4BD47A83325F}']
function f_dollar_to_euro(const p_dollar: Double): Double; s
tdcall;
procedure euro_to_dollar(const p_euro: Double;
var pv_dollar: Double); stdcall;
end; // i_client_euro_dollar
function f_i_client_euro_dollar: i_client_euro_dollar;
implementation
function f_i_client_euro_dollar: i_client_euro_dollar;
const k_service_url= 'http://localhost:8081/'
+ 'p_serveur_euro_dolar_wad.co_euro_dollar/soap/'
+ 'i_server_euro_dollar';
var l_c_http_remote_invokation_object: THTTPRIO;
begin
l_c_http_remote_invokation_object:= THTTPRIO.Create(nil);
Try
Result := (l_c_http_remote_invokation_object as i_client_euro_d
ollar);
l_c_http_remote_invokation_object.URL:= k_service_url
finally
if Result = nil
then l_c_http_remote_invokation_object.Free;
end; // try ... finally
end; // f_i_client_euro_dollar
initialization
InvRegistry.RegisterInterface(TypeInfo(i_client_euro_dollar),
'urn:u_i_server_euro_dollar-i_server_euro_dollar', 'utf-8');
InvRegistry.RegisterDefaultSOAPAction(TypeInfo(i_client_euro_do
llar),
'urn:u_i_server_euro_dollar-i_server_euro_dollar#
%operationName%');
end.

Notez que

les deux appels de InvRegistry correspondent aussi des enregistrement


"locaux" (pas dans la Registry Windows) et permettent de transformer nos
appels dei_client_euro_dollar en des requtes HTTP au format SOAP
le GUID de l'INTERFACE importe n'a rien voir avec le GUI sur
le Serveur. Ici aussi ce GUID sert uniquement identifier de faon unique
cetteINTERFACE dans la liste de correspondance locale au Client

4.4 - Utilisation de notre Proxy


Finissons par le programme Client. Nous allons simplement effectuer une
conversion d'une valeur contenue dans un tEdit, et la conversion sera effectue
lorsque nous cliquons sur un tButton:
slectionnez la tForm principale
placez un tEdit, un tLabel et un tButton sur Form1
crez l'vnement Button1Click et tapez le code qui convertira un montant (nous avons
utilis deux locales auxiliaires qui auraient pu tre vites):
procedure TForm1.dollar_to_euro_Click(Sender: TObject);
var l_euro_amount, l_dollar_amount: Double;
begin
l_dollar_amount:= StrToFloat(dollar_edit_.Text);
// -- appel du service
l_euro_amount:=
f_i_client_euro_dollar().f_dollar_to_euro(l_dollar_amount);
euro_label_.Caption:= FloatToStr(l_euro_amount);
end; // dollar_to_euro_Click
importez aussi U_I_CLIENT_EURO_DOLLAR dans la Forme principale
compilez, excutez, tapez un montant en dollar, cliquez sur le bouton
voici le rsultat:

Notez que

nous avons pu excuter directement notre Client, car notre Serveur tait
encore charg (mme si le projet Serveur a t ferm, le CGI reste lanc par
le Web AppDebbugger)

Le Web App Debugger stocke les messages changs entre le Client et le Serveur:
slectionnez l'icne du WAD sur la barre de tche
slectionnez l'onglet "Log"
la liste des messages HTTP est prsente:

cliquez sur l'avant dernier message, correspondant notre requte


le texte du message apparat dans une fentre, avec possibilit de visualiser les messages
prcdents, suivants etc

5 - Amliorations et Commentaire
5.1 - Compliqu ?
Il est craindre que la lecture de ce document ne fasse apparatre
les Services Web comme horriblement compliqus. Pourtant Delphi a fait des efforts
considrables pour limiter les manipulations au plus strict ncessaire. En ralit, le
fait que nous ayons pass du temps expliquer certains fonctionnement a
considrablement alourdi la prsentation.
Si vous crez deux ou trois services
les manipulations initiales (comme le premier lancement de ServerInfo, par
exemple) n'auront pas tre rptes
l'utilisation d'un groupe de projet simplifie normment les aller-retour
lorsque nous modifions l'INTERFACE du Service
les diffrentes manipulations deviendront automatiques

Nous avons mme simplifi le traitement en crant un programme de


transformation qui fonctionne ainsi:
nous avons crer un Service Web (Serveur et Client), en nommant le
"service" (l'quivalent de "euro_dollar") "xxx"
notre utilitaire copie les fichiers de cette application simple vers un autre
rpertoire en renommant "xxx" par le nom que nous avons fourni
Dans ce cas, il ne reste plus qu'
au niveau Serveur, entrer les mthodes du Service, et les implmenter
au niveau Client:
o copier les mthodes dans le client
o taper le code utilisateur du Client

5.2 - Conventions de noms


Nous avons utilis des convention de prfixes forts (i_xxx, u_i_xxx, c_xxx ainsi
que le mot "serveur" pour le Serveur et "client" pour le Client, et les suffixes
"_wad", "_cgi" et "_isapi" pour distinguer les diffrents types d'extension Serveur).
Comme d'habitude, vous n'tes pas obligs de suivre ces conventions. En
particulier, le fait de supprimer "serveur" et "client" dans le nom de
l'INTERFACE (par exemple iEuroDollar au lien
de i_serveur_euro_dollar et i_client_euro_dollar) permet quasiment d'utiliser la
mme INTERFACE pour le Serveur et le Client
Mais nous restons, quant nous, fidles aux prfixes. Dans ce cas, ils nous ont
permis de bien comprendre les relations entre les diffrentes pices qui entrent en
jeu. Si tous les identificateurs sont identiques, il est ais de croire qu'il s'agit d'un
objet alors qu'il s'agit d'un autre.
Et en utilisant l'utilitaire de transformation, les manipulation concernant les prfixes
sont quasiment inexistantes.

5.3 - Ce qui manque


Cet exemple simple avait pour unique but de montrer comment construire un
service web. Nous aurions pu ajouter
le traitement de types complexes (tableaux, CLASSes, tDataSets,
le transfert de donnes (fichiers) binaires
les pices attaches (SOAP Attachments)
les transferts de donnes au format .XML
les changes avec Datasnap, en utilisant des tSoapConnection
la cration d'extensions CGI et ISAPI
le dploiement sur des Serveurs HTTP (IIS, Apache)
le dbugging du Serveur Web Service en utilisant le Web App Debugger
les conversions entre des applications CGI, ISAPI et Web App Debugger
les diagrammes de Classe UML prsentant les diffrents
composants Delphi (tWebApp, tHttpRio ...) mis en oeuvre

5.4 - Formation Web Service


Les points mentionns ci-dessus sont abords lors de nos Formations XML Web
Services. Nous avons regroup la prsentation .XML avec la
prsentation ServicesWeb, car une connaissance du format .XML est utile, et il
arrive que les applications Service Web aient traiter des donnes au format .XML,
indpendemment de SOAP.

5.5 - Et .Net dans tout cela ?


Notre prsentation a t effectue en mode Win32. En fait
les Services Web correspondent un format, lequel peut tre implment et utilis

sur la plupart des ordinateur (Mac, PC), sous divers Operating Systems (Windows,
Linux, Unix, MacOs).
Par consquent ils peuvent aussi tre utiliss (Serveur et Client) dans
l'environnement .NET. Ce qui change ce sont les composants et les manipulations
de mise en oeuvre.
Celles-ci sont d'ailleurs prsentes lors de nos formations Delphi 2006 .Net ainsi
que pendant les formations ASP.NET Delphi et ASP.NET 2.0 Delphi

5.6 - Next
Au niveau explications, nous aurions aussi pu dtailler
le fonctionnement de WebBroker
la mcanique de iInvoke

Il existe une tape au-del des Services Web : les objets distribus. Pour expliquer la
hirarchie, nous avons
les extensions CGI, qui ncessitent un explorateur Web, l'appel d'une page
contenant une balise <FORM
les Services Web, que l'utilisateur peut invoquer depuis n'importe quelle
application excutable (pas besoin d'explorateur internet)
Ces services sont limits des appels de FUNCTIONs et
de PROCEDUREs. Les traitement raliss peuvent tre trs compliqu
(modifier une base de donne, retourner la carte de votre domicile), mais
comme il s'agit d'INTERFACE, la mcanique de base ne gre pas d'tat.
Une fois la rponse retourne, leServeur ne se souvient plus de vous. Si votre
traitement ncessite plusieurs tapes (lecture pagine de Tables), c'est vous
de grer ces sessions au niveauClient
les objets distribus: comme les objets contiennent des donnes, ils sont plus
puissants qu'une simple collection de mthodes
Nous prsentons les objets distribues lors de nos formations .Net

6 - Tlcharger le code source Delphi


Vous pouvez tlcharger:
web_service_tutorial.zip : l'application Serveur et l'application Client (15 K)
Ce .ZIP qui comprend:
les fichiers .DPR, PAS, DFM
le fichier .WSDL
Pour l'utiliser:
dzippez, slectionnez le groupe, compilez
Ce .ZIP ne modifie pas votre PC (pas de changement de la Base de Registre, de
DLL ou autre). Pour supprimer le projet, effacez le rpertoire.

Comme d'habitude:
nous vous remercions de nous signaler toute erreur, inexactitude ou
problme de tlchargement en envoyant un e-mail
jcolibri@jcolibri.com. Les corrections qui en rsulteront pourront aider les
prochains lecteurs
tous vos commentaires, remarques, questions, critiques, suggestion
d'article, ou mentions d'autres sources sur le mme sujet seront de mme
les bienvenus jcolibri@jcolibri.com.
plus simplement, vous pouvez taper (anonymement ou en fournissant votre
e-mail pour une rponse) vos commentaires ci-dessus et nous les envoyer
encliquant "envoyer" :
Nom :
E-mail :
Commentaires *
:

envoyer mes commentaires

et si vous avez apprci cet article, faites connatre notre site, ajoutez un
lien dans vos listes de liens ou citez-nous dans vos blogs ou rponses sur
les messageries. C'est trs simple: plus nous aurons de visiteurs et de
rfrences Google, plus nous crirons d'articles.

2 - La Premire Applications IntraWeb


Commenons par l'exemple simple de la calculette:
l'utilisateur tape l'adresse Web d'une page permettant de faire des multiplications
son explorateur web lui prsente une page avec 2 Edit et un bouton
l'utilisateur tape 2 nombre et clic le bouton
son explorateur lui renvoie le rsultat

Nous allons donc construire une page Web


avec deux Edit et un Bouton
lorsque l'utilisateur cliquera le bouton, nous calculerons le rsultat et
l'afficherons dans un label

Par consquent
crez sur votre disque un dossier 01_calculator
lancez Delphi 2006
lancez le Wizard Intraweb en slectionnant "File | New | Other | Intraweb | Intraweb
wizard"

L'emplacement des Wizards Intraweb dans les menus Delphi ainsi que le contenu des
diffrents dialogues varie avec les versions de Delphi, mais le mcanisme reste le mme
Intraweb prsente un dialogue permettant spcifier le type d'application Intraweb que
vous souhaitez crer

les valeurs par dfaut ("StandAlone", et "with Datamodule") conviennent. Ajoutez


simplement le chemin
C:\programs\fr\web\intraweb\01_calculator
et le nom de votre projet
p_01_calculator
et cliquez "Ok"
Intraweb cre 3 units

celle qui communiquera le Serveur Web

celle qui grera les sessions

et surtout l'unit correspondant la page que verra l'utilisateur

Ces fichiers sont visibles dans le gestionnaire de projet:

sauvegardez le projet et ses units avec des noms correspondant l'application dans le
bon rpertoire.
Cette tape peut tre saute, mais notre exprience de Delphi 2006 nous a appris
effectuer cette sauvegarde avant de poursuivre. Donc slectionnez chaque unit tour
tour et slectionnez "Save As" pour

sauvegarder ServerController sous le nom de u_01_server_controller

sauvegarder UserSessionUnit sous le nom de u_01_user_session_unit

sauvegarder Unit1 sous le nom de u_01_calculator

en vrifiant que chaque fichier va bien dans notre rpertoire


Puis, dans u_01_server_controler, dans le USES, changez le nom UserSessionUnit en
u_01_user_session_unit
Compilez pour vrifier que tout est cohrent
comme nous utilisons XP avec son pare feu Internet, la scurit Windows nous demande
si nous acceptons excuter ce programme

cliquez "Dbloquer"
le Serveur IntraWeb est affich

fermez ce Serveur (l'icne "x" en haut droite)


de retour dans Delphi, slectionnez la page u_01_calculator (pas le contrleur ou la
session).
Puis dans la Palette, slectionnez la page correspondant aux composants visuels "IW
Standard", puis slectionnez un tIwEdit

et posez-le sur la Forme.


Posez-en un second, puis, de la mme page de la Palette, un tIwButton et un tIwLabel
La Forme aura l'allure suivante:

crez l'vnement qui va calculer la multiplication en double-cliquant


sur IwButton1 (exactement comme pour une application Windows), et tapez le code qui
va effectuer le calcul
procedure TIWForm1.IWButton1Click(Sender: TObject);
begin
IwLabel1.Caption:=

IntToStr(StrToInt(IwEdit1.Text)* StrToInt(IwEdit2.Text));
end; // IWButton1Click
compilez
le Serveur Internet Intraweb est affich:

lancez l'explorateur Web

soit en cliquant sur l'icne dsigne par la flche rouge

soit en tapant F9

soit par le menu "File | Execute"

Internet Explorer est lanc, et notre page est prsente:

tapez deux nombres, par exemple 3 et 5 et cliquez "IwButton1"


le rsultat est affich dans Internet Explorer

Notez que:
nous avons utilis IntraWeb en mode "StandAlone": notre application
contient en fait un Serveur Web qui est charg de fournir notre page
Internet Explorer. Il existe un autre mode, dit mode "Page" dont nous
parlerons la fin de notre prsentation
comme indiqu, l'emplacement dans les menus Delphi du Wizard qui lance
le serveur Standalone peut varier normment selon la version de Delphi.
Mais en naviguant un peu dans ces menus, il n'est gure difficile de trouver
la page Intraweb, et sur cette page une icne correspondant au mode
Standalone
lorsque nous excutons le projet et lanons le Serveur (F9 ou l'icne verte), il
ne faut pas trop tarder remplir la page et cliquer sur le bouton. Il y a en
effet une temporisation qui peut bloquer la rponse. Dans ce cas relancez
l'excution Delphi

Nous allons prsent examiner le principe du fonctionnement Intraweb, puis


prsenter quelques exemples d'applications simples utilisant Intraweb.

3 - Le fonctionnement Intraweb
3.1 - Fonctionnement Internet
Un petit rappel rapide sur le fonctionnement des sites Web:
un dveloppeur crit des fichiers avec une syntaxe spciale (.HTML) correspondant
ses pages. Voici par exemple une page simple avec un texte de bienvenue:
<HTML>
<BODY>
Bienvenue sur notre site
</BODY>
</HTML>
La page, qui peut tre rdige en utilisant NotePad (ou tout autre diteur de texte ASCII)
est sauvegarde sur disque, sous le nom de HELLO.HTML, par exemple

le dveloppeur lance un Serveur Web, qui est une application qui coute et attend les
requtes provenant de clients. Ce serveur peut tre IIS (InternetInformation Server),
Apache, un serveur Web crit en utilisant des Sockets Delphi:

un utilisateur lance un explorateur web (Internet Explorer, Netscape, un client Web crit
avec des Sockets Delphi, ...), et demande l'explorateur web de rapporter la page
HELLO.HTML:

le Serveur Web localise HELLO.HTM, l'envoie vers l'explorateur, qui l'affiche:

3.2 - Fonctionnement Intraweb en mode Application


Si nous utilisons Intraweb,
la construction du texte .HTML de la page sera fait de faon visuelle en
posant des composants sur la Forme. Les proprits de ces composants
seront utilises par Intraweb pour gnrer le fichier .HTML
le Serveur Web est un Le fichier .HTML rsultant

Ceci peut tre schmatis ainsi:


le dveloppeur charge Delphi avec les composants Intraweb:

en utilisant la Palette, l'Inspecteur d'Objet et les Formes, il construit ses pages et


compile l'application, qui est place sur disque, dans un fichier nomm, par exemple,
HELLO.EXE:

cette application est lance, et elle correspond un Serveur Web Intraweb, plus les
paramtres des pages construites:

un utilisateur lance un explorateur web (Internet Explorer, Netscape, un client Web crit
avec des Sockets Delphi, ...), et demande l'explorateur web de rapporter la page
HELLO.HTML:

le Serveur Web Intraweb utilise les paramtres de la Forme pour construire la page
HELLO.HTML, et la retourne au client qui l'affiche:

La gnration d'une page .HTML partir d'un Forme n'est en fait pas trs
complexe: il suffit d'analyser les proprits des contrles et gnrer les balises
.HTML (<BODY>, <INPUT> etc) correspondantes. Nous avons d'ailleurs prsent
dans l'article Delphi Web Designer un projet Delphi permettant de gnrer une
page .HTML l'aide de contrles Delphi (tPanel, tEdit, tLabel ...) poss sur
une tForm. Notre gnrateur est bien entendu trs simplet par rapport aux
contrles IntraWeb, mais il permet de comprendre le principe. De plus, Intraweb a
ajout le Serveur Web, et mme le lancement de l'Explorateur Web.

3.3 - Architecture IntraWeb en mode Application


Voici la structure d'un projet IntraWeb en mode Application:

et:
nous utiliserons le Serveur Web IntraWeb (plutt que les
composants WebBroker)
ce Serveur est construit l'aide des composants Indy. Ceci explique d'ailleurs
les possibilits SSL (Secure Socket Layer, ou encore mode TCP/IP scuris)
offertes par Intraweb
IwServerController est charg de grer l'application, et en particulier
o les sessions utilisateur
o les exceptions
Voici un diagramme de classe UML simplifi (les
classes Intraweb reprsentes sont celles de Delphi 6):

Les pages sont appeles par un Explorateur Web en utilisant une URL ayant
la structure suivante (visible sur la capture d'cran ci-dessus):

et:
o le port est spcifi par tIwServerController.Port
o le type de commande est gr par tIwServerController.ExecCmd
o chaque session a un identificateur (calcul, entre autres, l'aide de la
date), utilis par les cookies ou les champs cachs
tIwAppForm est l'anctre de toutes nos formes. Cette classe sert de conteneur
pour tous les contrles et permet de dfinir les proprits des pages. Le
diagramme de classe simplifi est le suivant:

pour mettre en page nos Formes, tIwAppForm utilise un descendant


de tIwLayoutMgr

Si aucun tIwLayoutManager n'est plac sur la Forme, un gestionnaire de


position est cr par dfaut.

3.4 - Forme CGI


Nos schmas ci-dessus ont prsent la mcanique de requte de page par
un Explorateur, et la rponse .HTML du Serveur Web.
Lorsque la page .HTML contient des zones de saisie (texte, checkbox, bouton
radio, listbox), il y a un double aller-retour entre l'Explorateur et le Serveur.
Tout d'abord, il faut comprendre que la mcanique utilise pour qu'un utilisateur
change des informations avec un Serveur est l'antique technique des <FORM>
.HTML. Elle fonctionne ainsi:
le dveloppeur place dans son texte .HTML une balise <FORM> l'intrieur de laquelle
se trouvent des balises pour:

au moins un bouton

des contrles de saisie. Par exemple un Edit

Voici une telle page pour convertir des Euros en Dollars:

et le texte .HTML correspondant est le suivant:

<HTML>
<HEAD>
</HEAD>
<BODY>
<FORM METHOD="POST"
ACTION="http://www.jcolibri.com/convert.exe">
valeur en euro ?
<INPUT TYPE="edit"NAME="les_euros"><BR>
<INPUT TYPE="submit"VALUE="convertis"><BR>
</FORM>
</BODY>
</HTML>
o

<INPUT TYPE="edit" ...> est l'Edit

<INPUT TYPE="submit" ...> est le bouton

<FORM METHOD="POST" ACTION="http://www.jcolibri.com/convert.exe">


est la balise qui dfinit le formulaire Web. Cette balise indique quel est le logiciel
sur le Serveur qui traitera la conversion: CONVERT.EXE

le dveloppeur cre donc ce logiciel. Le contenu de ce type de programme a t prsent


dans l'article CGI Form. Ce type de logiciel est donc plac sur leServeur Web:
le Serveur Web est lanc (IIS, Netscape ...)
Voici alors la situation:

un utilisateur lance son Explorateur, et demande la page EURO.HTML:

le Serveur retourne la page avec la <FORM>:

l'utilisateur entre un montant (123, par exemple) et clique le bouton "convertis"

le Serveur

analyze les paramtres (la valeur 123)

charge CONVERT.EXE et lui transmet les paramtres

CONVERT.EXE effectue la conversion, et construit une nouvelle page .HTML.


Supposons que cette rponse contienne uniquement la valeur ne dollars ($ 166).
Cette page est renvoye au Serveur

le Serveur retourne cette page .HTML l'utilisateur

et l'Explorateur affiche la rponse:

Le point extrmement important est que le traitement du "clic" est effectu sur
le Serveur. Cette mcanique est cheville dans le protocole .HTML, et est identique
que nous mettions en oeuvre CGI, ISAPI, NSAPI, ASP, ASP.NET ou ... Intraweb.
Pour rsumer

l'utilisateur rcupre une <FORM> .HTML


il remplit les diffrentes informations (Edit, CheckBox etc) et envoie les
donnes qu'il a fourni en cliquant un Bouton
le Serveur rcupre ces informations et en fait ce qu'il veut. En gnral, il
renvoie au moins un accus de rception, ou bien retourne la mme page
mise jour, ou encore renvoie un autre page .HTML

Lorsque nous utilisons une page .HTML construite avec Intraweb et qui contient
des contrles, la mcanique est la mme, sauf que
le Serveur Web est le Serveur Intraweb
ce Serveur est utilis pour envoyer toutes les pages .HTML: aussi bien la
page initiale, que les pages calcules en rponse aux valeurs retournes par
l'Explorateur clique sur un Bouton. Et c'est le code situ dans
l'vnement OnClick du bouton qui est utilis pour effectuer la suite des
traitements (mise jour d'une base de donnes, intgration des valeurs dans
une rponse etc)
tous les calculs se font donc sur le Serveur, et par du code Delphi usuel. Vous
avez donc accs tout: les PROCEDUREs, les CLASSes, les UNITs, les
bases de donnes etc. Et dans un langage dcent, pas dans un langage de
script la syntaxe ahurissante.

Le schma final devient dans ce cas:

Notez que:

TOUTES nos pages, plus le Serveur Intraweb sont contenus dans un seul
.EXE (en mode Application).

Nous avons prsent la mcanique CGI pour bien illustrer le fonctionnement sousjacent. Mais vous n'aurez pas programmer de CGI, ISAPI, d'crire de balises
.HTML etc: c'est Intraweb qui mettra tout en place. C'est ce que nous allons
examiner prsent.

4 - Quelques Techniques IntraWeb


4.1 - Exemples IntraWeb
Maintenant que nous avons prsent un premier exemple ainsi que le
fonctionnement de base d'IntraWeb, nous allons examiner quelques exemples un
peu plus labors:
navigation entre plusieurs pages - change de donnes entre pages
utilisation de contrles lies aux bases de donnes (BDE, Interbase, ADO)
affichage dans des tIwDbEdit, tIwDbGrid
modification de tIwDbGrid

4.2 - Navigation entre plusieurs pages


4.2.1 - Transfert de page
Un site est en gnral compos de plusieurs pages .HTML, et l'utilisateur navigue
d'une page une autre en utilisant des hyper-liens ou des boutons.
Dans le cas d'Intraweb
nous crons autant de pages Intraweb additionnelles que nous souhaitons en
utilisant "File | New | Other | Intraweb | New Form"

Intraweb gre une pile de pages actives, et


o au lancement c'est la page par dfaut qui est ajoute la pile
o lorsque l'utilisateur clique un bouton, le Serveur prend la main, en fait
dans le OnClick du bouton de la page clique. Pour que
le Serveur renvoie une page vers l'utilisateur, il faut
crer la seconde page
var my_iw_page: t_my_iw_page;
my_iw_page:= t_my_iw_page.Create(WebApplication);

l'ajouter au sommet de la pile en appelant:


my_iw_page.Show

o ce qui peut, dans ce cas de "cration + affichage" tre abrg par:


t_my_iw_page.Create(WebApplication).Show

o lorsque nous souhaitons qu'une page ne soit pas retourne


l'utilisateur, nous appelons:
my_iw_page.Hide

o et
la page est retire de la liste des pages actives
la page situe au sommet de la pile est retourne l'utilisateur
Notez que:
les nouvelles pages sont ajoutes par "File | New | Other | Intraweb | New
Form", PAS en ajoutant une nouvelle Forme Windows ("File | New | Form")

pour retourner une nouvelle page, il FAUT utiliser iw_page.Show(), et


jamais ShowModal. De mme pour supprimer une page, il faut
utiliser iw_page.Hide() (et pas Visible:= False). Ce sont en effet ces deux
primitives Show() et Hide() qui grent la pile des pages IntraWeb

4.2.2 - Exemple de transfert de page


Voici un exemple avec deux pages: la premire calcule le taux du dollar, la seconde
renvoie la valeur.
Commenons par crer une application IntraWeb:
lancez une nouvelle application IntraWeb par "File | New | Other | Intraweb | Intraweb
Application Wizard"
le wizard est affich
tapez le chemin et le nom de l'application, par exemple P_02_TWO_PAGES. et cliquez
"OK"
la nouvelle application est cre
modifiez aussi le nom de UNIT1 en U_02_TWO_PAGES_1, et le nom
de tIwForm1 en tIwForm_page_1
compilez pour vrifier

Ajoutons la seconde page:


slectionnez "File | New | Other | Intraweb | New Form"

un dialogue de slection de type de page est affich:

slectionnez "Application Form", et cliquez "Ok"


une nouvelle unit avec sa tIwForm est cre
renommez l'unit U_02_TWO_PAGE_2, et la tIwForm tIwForm_page_2
compilez pour vrifier

Garnissons la premire page


slectionnez U_02_TWO_PAGES_1
de l'onglet "IW Standard" de la Tools Palette, slectionnez un tIwLabel posez-le sur
la Forme, et changez son Caption en "valeur en euros"
de l'onglet "IW Standard", slectionnez un tIwEdit, et initialisez Text une valeur par
dfaut, par exemple 123
de l'onglet "IW Standard", slectionnez un tIwButton, nommez-le "convert", crez son
vnement OnClick, et calculez la valeur en dollar (multiplication par 1.35 environ), et
effectuez le transfert de page:
procedure TIWForm_page_1.convertClick(Sender: TObject);
var l_euro, l_dollar: Double;
l_c_iwform_page_2: tiwform_page_2;
begin
l_euro:= StrToFloat(IwEdit1.Text);
l_dollar:= l_euro* 1.35;
l_c_iwform_page_2:= tiwform_page_2.Create(WebApplication);
l_c_iwform_page_2.iwlabel1.Caption:= IwEdit1.Text
+ ' euros = '+ FloatToStr(l_dollar)+ ' US $';
l_c_iwform_page_2.Show();
// -- same as
// with tiwform_page_2.Create(WebApplication) do
// begin
// iwlabel1.Caption:= IwEdit1.Text+ ...ooo...
// Show;
// end;
end; // convertClick

Ajoutez aussi, aprs IMPLEMENTATION l'importation de la page 2:


implementation
uses u_02_two_pages_2;

Et pour que la seconde page permette de revenir la page 1 pour entrer une
nouvelle valeur:
slectionnez U_02_TWO_PAGES_2
de l'onglet "IW Standard" de la Tools Palette, slectionnez un tIwLabel posez-le sur

la Forme
de l'onglet "IW Standard", slectionnez un tIwButton, nommez-le "backbutton", crez
son vnement OnClick, et retirez la seconde page de la pile
procedure TiwForm_page_2.backbuttonClick(Sender: TObject);
begin
Hide();
// Release();
end; // backbuttonClick
compilez et excutez
le Serveur est affich
clickez l'icne pour lancer l'Explorateur
la page 1 est affiche

tapez une valeur et cliquez "convert"


la seconde page est affiche

cliquez "backbutton"
la premire page est affiche nouveau:

Notez que
l'URL affiche un nombre d'aller et retour croissant: 0, 1, 2

le SessionId en revanche reste le mme


pour une raison inconnue, nous ne pouvons pas utiliser des Name comportant
un soulign "_"

4.2.3 - Architecture de la pile des pages


Au niveau architecture, la CLASS tIwApplication gre les diffrentes pages
(formes):

et lorsqu'un nouvel utilisateur se connecte, tIwControllerBase lance une nouvelle


session, ce qui cre une nouvelle tIwApplication avec une nouvelle liste de pages, et
une page active:

et par la suite, cet utilisateur ajoute ou retire des pages actives sa liste par Show()
et Hide().

4.2.4 - Cration et Destruction


Show() et Hide() ne font que manipuler la prsence de la page au sommet de la pile,
et ne grent ni la cration, ni la suppression de l'objet.
Pour crer une page, il faut utiliser, comme nous l'avons indiqu ci-dessus:
t_my_iw_page.Create(WebApplication)

et pour librer une page:


my_iw_page.Release

En fait dans l'exemple ci-dessus nous avons utilis page_2.Hide(), alors que nous
aurions du employer page_2.Release()
Comme chaque page a un rfrence vers tIwApplication qui contient la liste des
pages, il est possible, titre de debugging, de visualiser cette liste:

slectionnez U_02_TWO_PAGES_1
de l'onglet "IW Standard" de la Tools Palette, slectionnez un tIwListBox et posez-la sur
la Forme
slectionnez un tIwButton, nommez-le "pagestack", crez son vnement OnClick, et
affichez la liste des pages (excut par le Serveur lorsqu'il traiterapagelist.Click)
procedure TIWForm_page_1.pagestackbuttonClick(Sender: TObje
ct);
var l_form_index: Integer;
begin
with IwListBox1 do
begin
Clear;
for l_form_index:= 0 to WebApplication.FormCount- 1 do
Items.Add(WebApplication.Forms[l_form_index].Name);
end; // with iwIlistBox1
end; // pagestackbuttonClick
compilez, excutez et lancez l'Explorateur
cliquez sur "pagestackbutton"
notre page est affiche, ainsi qu'un item "--no_selection--" et IwUserSession (qui est
le DataModule cr par dfaut par le Wizard)

cliquez trois fois sur "convert" et "backbutton", et sur "pagelistbutton"


la pile des page contient bien plusieurs pages 2:

Maintenant que vous comprenez la mcanique, plusieurs solutions sont possibles:


dans la seconde page, dans backbutton.Click, utiliser Release() au lieu
de Hide()
placer la variable l_c_page_2 dans TIWForm_page_1, et ne crer la seconde
page que si cet attribut est NIL
placer l_c_page_2 dans un tDataModule associ la session (cf plus bas)
Notez aussi que
une mme page (instance de tIwForm_nnn) peut tre rfrence plusieurs
fois dans la pile des pages. Cela peu se produire lorsque l'application exige
que cette page soit prsentes plusieurs reprises aprs des visites d'autres
pages. Si nous appelons Release(), toutes ces instances seront libres
une fois une tiwForm cre, elle existe tant que vous ne la supprimez pas
par t_iwForm.Release(). Il est donc trs simple de transfrer des donnes
d'une page l'autre en accdant ses contrles (ce que nous avons fait en
initialisant page_2.Label depuis page_1.ButtonClick), ou des
attributs PUBLIC

comme l'ensemble des pages utilise des Threads, il ne faut PAS utiliser des
variables globales. Plusieurs alternatives sont possible
o utiliser les attributs des pages en mmoire
o utiliser les tDataModules associs la session (cf plus bas)
o en dernier recours, utiliser des tThreadVars

4.2.5 - Lancement depuis un Client distant


Pour le moment nous avons affich nos pages via le Serveur Standalone.
Naturellement nos utilisateurs seront sur une machine loigne, et c'est eux qui
lanceront la demande d'une page de notre site.
En fait, il suffit dans Internet Explorer de fournir l'adresse et le port (si ce n'est pas
le port 80) pour obtenir la page principale, puis de naviguer partir de l.
Nous utilisons l'Institut Pascal un rseau local, et le poste sur lequel est
le Serveur Intraweb a l'adresse IP 152.178.41.18 (lancez ipconfig.exe pour
ventuellement connatre l'adresse de votre poste). Pour effectuer notre essai, il faut
connatre le port. Par consquent
compilez votre projet et lancez le Serveur Standalone
slectionnez, dans cette application "Tools | Start URL"
l'URL est copie dans le presse-papier
copiez cet URL dans un fichier ASCII
le port est 8888
allez sur un autre poste du rseau, lancez Internet Explorer et dans la barre d'adresse,
tapez
http://152.178.41.18:8888/ Entre
la page est affiche dans Internet Explorer, comme prcdemment

4.3 - Bases de Donnes


4.3.1 - Un tdbEdit et une Table BDE (dBase)

Commenons par afficher dans des dbEdit les champs de la Table ANIMALS.DBF.
Nous allons
placer les composants d'accs aux bases de donnes sur
le tDataModule UserSession
initialiser la connection avec la base
ajouter la tIwForm quelques champs tIwDb_xxx
Par consquent:
lancez une nouvelle application IntraWeb par "File | New | Other | Intraweb | Intraweb
Application Wizard"
le wizard est affich
tapez le chemin et le nom de l'application, par exemple P_03_DBEDIT et cliquez "OK"
la nouvelle application est cre
modifiez aussi le nom de UNIT1 en U_03_DBEDIT
compilez pour vrifier

Puis nous armons une Table


slectionnez UserSessionUnit et slectionnez la forme (onglet "Design" en bas)
initialisez une tTable

de l'onglet "BDE" de la Tool Palette, slectionnez une tTable et posez la sur la


forme tIwUserSession

dans l'Inspecteur
o initialisez DataBaseName avec DBDEMOS
o initialisez TableName avec ANIMALS.DBF
o basculez Active sur True

de l'onglet "DataAccess" de la Palette, slectionnez une tDataSource et posez la sur la


forme tIwUserSession. Initialisez DataSet vers Table1

Ajoutons un tIwDbEdit pour afficher un champ:


slectionnez U_03_DBEDIT, slectionnez le code
pour que les composants db_xxx puissent accder DataSource1, ajoutez la
clause USES l'importation de UserSessionUnit

slectionnez la forme
slectionnez de l'onglet "IwData" de la Palette, et dans cette page
slectionnez un tiwDbEdit:

et posez-le sur la Forme


Dans l'Inspecteur
o slectionnez DatSource. Si vous avez bien import UserSessionUnit,
alors IWUserSession.DataSource1Data sera affich. Slectionnez cette
source
o slectionnez DataField, et choisissez un champ, par exemple AREA
compilez et excutez
voici le rsultat:

Tant qu'a faire, nous pouvons ajouter un navigateur et afficher quelques images:
de l'onglet "IwData" de la Palette, slectionnez un tIwDbNavigator, posez-le
sur le Forme, et initialisez DataSource vers DataSource1
de cette page slectionnez un tiwDbImage, posez-le sur la Forme et
initialisez
o DataSource vers DataSource1
o DataField vers BMP
compilez et excutez
voici le rsultat:

4.4 - tIwDbGrid et la base de donnes Interbase


4.4.1 - Utilisation d'une DataGrid
Pour varier les plaisirs, voici une application avec une grille et un moteur
SQL Interbase. Tout d'abord, crons un nouveau projet:
lancez une nouvelle application IntraWeb par "File | New | Other | Intraweb | Intraweb
Application Wizard"
le wizard est affich
tapez le chemin et le nom de l'application, par exemple P_04_DBGRID et cliquez "OK"
la nouvelle application est cre
modifiez aussi le nom de UNIT1 en U_04_DBGRID_INTERBASE
compilez pour vrifier

Puis nous armons une Table, par exemple EMPLOYEE.COUNTRY


dans le Gestionnaire de Projet, slectionnez UserSessionUnit et slectionnez la forme
(onglet "Design" en bas)
initialisez une tTable

de l'onglet "Interbase" de la Tools Palette, slectionnez une tIbDataBase et


posez-la sur la Forme.

Cliquez deux fois sur IbDatabase1 pour ouvrir l'Editeur de Connection, et


initialisez ses proprits vers votre base. Dans notre cas:
o DataBase vers
C:\Program Files\Fichiers communs\Borland
Shared\Data\EMPLOYEE.GDB
o User Name SYSDBA
o Password masterkey
o Login False
Testez, et validez par "Ok"

de l'onglet "Interbase" de la Palette, slectionnez une tIbTransaction et reliez-la


IbDatabase1 et rciproquement

de l'onglet "Interbase" de la Palette, slectionnez une tIbQuery, et initialisez


o DataBase vers IbDatabase1
o SQL avec une slection de Table, par exemple

SELECT * FROM country


o basculez Active True

de l'onglet "Data Access" de la Palette, slectionnez une tDataSource, posez-la


sur la tForm et initialisez DataSet vers IbQuery1

voici la vue de notre tDataModule:

Ajoutons un tIwDbGrid pour afficher la Table:


slectionnez U_04_DBGRID_INTERBASE, slectionnez le "Code"
pour que les composants db_xxx puissent accder DataSource1, ajoutez la
clause USES l'importation de UserSessionUnit
slectionnez la forme
slectionnez de l'onglet "IwData" de la Palette, et dans cette page
slectionnez un tiwDbGrid et posez-la sur la tForm
Dans l'Inspecteur slectionnez DatSource. Si vous avez bien import
UserSessionUnit, alors IWUserSession.DataSource1Data sera affich.
Slectionnez cette source
compilez et excutez
voici le rsultat:

4.4.2 - Techniques d'affichage d'une tIwDbGrid

Nous pouvons utiliser plusieurs proprits pour varier l'affichage dans


une tIwDbGrid:
nous pouvons alterner les couleurs des lignes en utilisant RowAlternateColor
la couleur de la ligne au-dessus de laquelle passe la souris peut tre modifie
par RollOver, RollOverColor
la couleur de la ligne slectionne
Voici quelques exemples:
posez une autre tIwDbGrid sur la tForm, et liez DataSource vers DataSource1
slectionnez RowAlternateColor et donnez-lui la valeur clSkyBlue par exemple
slectionnez RowHeaderColor, et choisissez, par exemple clWebBisque
slectionnez RowCurrentColor, et affectez-lui clWebLime
slectionnez RollOver, et basculez la valeur sur True, et affectez RollOverColor la
valeur clWebYellow
compilez et excutez
voici le rsultat:

4.4.3 - Pagination
Lorsque la Table est de grande taille, il est coteux et inefficace de transfrer vers le
client toutes les lignes. Nous pouvons limiter le nombre de lignes envoyes en

utilisant RowLimit, et en grant par des boutons la demande de la page prcdente


ou suivante.
Par exemple:
posez une autre tIwDbGrid sur la tForm, et

liez DataSource vers DataSource1

basculez FromStart False

donnez RowLimit une valeur telle que 4

posez un tIwButton sur la Forme, crez son vnement OnClick et tapez le code qui
dplace la ligne courante d'un nombre de lignes gale au nombre de lignes de
le tIwDbGrid:
procedure TIWForm1.next1Click(Sender: TObject);
begin
UserSession.IbQuery1.MoveBy(IwDbGrid3.RowLimit);
end; // next1Click
Importez aussi dans la liste des USES SERVERCONTROLLER pour pouvoir accder
USERSESSION.IbQuery1
posez de mme un bouton pour reculer
compilez et excutez
voici le rsultat :

Notez que
IbQuery1 est sur le DataModule USERSESSION. Comme indiqu plus haut,
il n'y a PAS de variable globale nous permettant de dsigner ce DataModule.
En revanche, ServerController a cr une FUNCTION,
appele UserSession, qui nous permet d'accder aux composant
de TIWUserSession
vous pouvez aussi ajouter le code qui inhibe les boutons lorsque nous
dpassons les limites (IbQuery.Eof etc)
le lecteur attentif aura remarqu que dans la dbGrid2, la couleur jaune
souligne bien que c'est "japan" qui est devenue la ligne courante
comme nous ne pouvons utiliser un IwButton1.Name tel que "next_", nous
avons utilis "next1". Le risque est en effet, dans des WITH ventuels,
d'utiliser le nom Next ayant une signification pour d'autres composants
faisant partie du WITH. Si vous n'employez pas de WITH, nommez les
boutons comme bon vous semble.

4.4.4 - tDbGrid modifiable


Nous allons examiner comment l'utilisateur peut changer les valeurs prsentes
dans un grille. Nous allons en profiter pour montrer comment utiliser une
connection ADO.
Commenons par crer l'application:
lancez une nouvelle application IntraWeb par "File | New | Other | Intraweb | Intraweb
Application Wizard"
le wizard est affich
tapez le chemin et le nom de l'application, par exemple P_05_DBGRID_ADO et cliquez
"OK"
la nouvelle application est cre
modifiez aussi le nom de UNIT1 en U_05_DBGRID_ADO
compilez pour vrifier

A prsent armons une Table ADO


Ici, nous allons utiliser le fichier ACCESS fourni avec Delphi, appel
DBDEMOS.MDB. Ce fichier est situ, dans notre cas, en
C:\Program Files\Fichiers communs\Borland Shared\Data\
et nous l'avons copi dans un rpertoire plus confortable, appel \_data, proche de
notre application
Voici comment utiliser une Table ADO:
slectionnez le DataModule USERSESSIONUNIT, et sa tForm ("Design")
de l'onglet "DbGo" de la Tools Palette, slectionnez tAdoConnection et posez-le sur
la tForm.
Dans l'Inspecteur, slectionnez Connection et cliquez sur l'ellipse
l'Editeur de connection ADO de Microsoft est prsent.
nous allons construire notre propre chane de connection, et nous slectionnons "use
connection string | Build"
le constructeur de chanes de connection Microsoft est prsent
comme nous allons utiliser le moteur Sql ServerADO, nous slectionnons "Fournisseur |
Microsoft 4.0 OleDb Provider", puis cliquons "Suivant"
la page "Connexion" est prsente
nous cliquons l'ellipse ct de "slectionner la base de donnes" et allons slectionner
dbdemos.mdb.
nous cliquons "tester la connexion", qui russit, et fermons l'diteur par "ok"
dans la Palette, nous slectionnons un tAdoDataset et le posons sur la tForm. Nous
initialisons Connection vers AdoConnection1, puis slectionnonsCommandText et

cliquons sur l'ellipse


l'diteur de requtes est prsent
tapez la mme requte que ci-dessus (dbdemos.mdb contient la mme table que
employee.gdb):
SELECT * FROM country
basculez AdoDataSet.Active True
pour pouvoir modifier la Table

posez un tAdoCommand sur la Forme

initialisez Connection vers AdoConnection1

basculez CheckParams False

Voici comment utiliser la Table ADO avec Intraweb:


slectionnez U_02_EDIT_DBGRID_ADO
pour que les composants db_xxx puissent accder DataSource1, ajoutez la
clause USES l'importation de UserSessionUnit
de l'onglet "Data Access" de la Palette, slectionnez une tDataSource, posez-la sur
la tForm et initialisez DataSource1.DataSet vers AdoDataSet1
de l'onglet "Iw Data", selectionnez une tIwDbGrid et posez-la sur la Forme.
Slectionnez DatataSource et initialisez-la vers DataSource1
pour que ADO fonctionne, il faut aussi s'occuper de l'initialisation des objets COM:

slectionnez SERVERCONTROLLER.PAS

dans l'Inspecteur,
initialisez IwServerController.ComInitialization ciNormal ou ciMultiThreaded

finalement, pour permettre la modification d'une ligne,

ajoutez un composant tAdoCommand la tForm, et


initialisez Connection vers AdoConnection1. Et il faut
basculer ParamCheck sur False

vous pouvez aussi utiliser un tAdoDataset, qu'il faut galement initialiser


vers AdoConnection1

titre de vrification, lancez l'application et vrifiez que vous voyez bien la table

Notez que:
il faut basculler ComInitialization. Si cette tape n'est pas effectue, vous
aurez une erreur "coinitialize" qui est, pour les utilisateurs d'ADO
avec IntraWeb, l'erreur la plus frquente. Cette proprit n'est pas initialise
par IntraWeb par dfaut, car elle n'a lieu d'tre que pour ADO
nous avons aussi montr ici que nous pouvons placer le tDataSource sur
la TIWForm1 (comme nous pouvons choisir de le placer sur tIwUserSession)
si la connexion ADO ne russit pas, vous pouvez faire l'exercice avec une
Table BDE, InterBase, ou tout autre composant d'accs
Puis limitez le nombre de lignes et posez les tIwButton pour naviguer dans la grille,
comme il a t prsent dans le paragraphe prcdent.

Maintenant occupons nous de la modification d'une tIwDbGrid. Apparemment ce


n'est pas prvu de faon automatique. Plusieurs solutions sont possibles
utiliser un tIwDbGrid uniquement pour l'affichage, et employer
un tIwNavigator pour synchroniser l'affichage entre la tIwDbGrid et
les tIwDbEdits. Ce n'est pas une solution totalement irraliste: si vous
regardez VOYAGES.SNCF.COM, il n'y a pas de DataGrid, mais des pages
avec des liens clicables, ou des botes d'dition pour entrer vos coordonnes
et payement
utiliser un tIwData, le peupler et synchroniser l'affichage et la mise jour
avec la Table
grer en parallle des structures qui mmorisent o nous sommes et lancent
les UPDATE en fonction de cette position
C'est cette dernire solution que nous avons retenue. Elle s'inspire en fait
de ASP.NET, ou, lorsque nous souhaitons modifier une grille. Dmontrons d'abord
schmatiquement ce que nous souhaitons faire:
prenons une grille contenant 3 colonnes utiles:

nous lui adjoignons 3 colonnes pour grer la modification.


en mode prsentation, la grille comporte une colonne de boutons permettant
de mettre une ligne en mode modification:

lorsque l'utilisateur a slectionn une ligne en cliquant sur le bouton de cette


ligne, une nouvelle grille lui est prsent comportant cette fois-ci
o des Edit qui permettent de modifier le contenu d'un champ
o deux boutons permettant de confirmer ou annuler
Voici notre grille en mode modification

An niveau IntraWeb:
nous commenons par ajouter 3 colonnes pour nos boutons. Cela se fait en
crant les colonnes de tIwDbGrid manuellement, en utilisant la
proprittIwDbGrid.Columns

l'incrustation de tIwButtons et tIwEdits se fait en grant l'affichage de chaque


cellule. Pour cela la tIwDbGrid comporte un
vnement tIwDbGrid.OnRenderCell(similaire aux
vnement OwnerDraw d'une tDbGrid usuelle):
procedure OnIWDBGridRenderCell(ACell: TIWGridCell;
const ARow, AColumn: Integer);

Cet vnement nous fournit


o la ligne et la colonne que nous devons dessiner
o la cellule courante, qui a un paramtres tIwGridCell.Control, que nous
utilisons pour incruster les boutons ou edits
nous crons un vnement modify_button_clic qui nous permet de mettre
la tIwDbGrid en mode dition (et rcuprer les informations sur le numro
de la ligne, la cl etc).
nous crons les vnements cancel_button_clic pour annuler tout
changement, et rafficher la grille en mode "affichage"
et finalement un vnement post_button_clic, qui
o ira rcuprer les informations sur la ligne courante et les nouvelles
valeurs dans les tIwEdit pour construire une requte SQL UPDATE
o excutera cette requte
o remettra la tIwDbGrid en mode affichage

Voici donc notre version


slectionnez IwDbGrid1, slectionnez sa proprit Columns et cliquez sur l'ellipse ...
un Editeur de colonnes est affich
cliquez sur l'icne d'ajout (ou "clic droit | add") et ceci 5 fois (pour la colonne de
modification, 2 colonnes de donnes et 1 colonne pour Post, une pour Cancel)
affichez le "Code" et dclarez dans la partie PUBLIC de tIwForm1 les boutons et les
edits qui seront ncessaires, ainsi que les donnes de suivi de la ligne slectionne:
type TIWForm1=
class(TIWAppForm)
DataSource1: TDataSource;

IWDBGrid1: TIWDBGrid;
next1: TIWButton;
previous1: TIWButton;
// -- ...ooo...
public
m_c_do_modify_button_list: array of tIwButton;
m_c_do_post_button, m_c_do_cancel_button: tIwButton;
m_c_edit_list: array of tIwEdit;
m_do_modify: Boolean;
m_selected_row: Integer;
m_selected_key: String;
procedure handle_do_modify_click(Sender: tObject);
procedure handle_do_cancel_click(Sender: TObject);
procedure handle_do_post_click(Sender: TObject);
end; // TIWAppForm

et:

m_c_do_modify_button_list: la liste verticale des boutons pour slectionner une


ligne

m_c_do_post_button, m_c_do_cancel_button: les boutons qui


excutent Post ou Cancel

m_c_edit_list: la liste horizontale des tIwEdit permettant de modifier les champs


de la ligne slectionne

m_do_modify: le boolen qui gre l'tat affichage / modification de la grille

m_selected_row, m_selected_key: les paramtres permettant de construire la


requte

crez l'vnement tIwForm.OnCreate, et crez les do_edit boutons verticaux,


les edits horizontaux et les bouton Post et Cancel:
const k_table_column_count= 2;
procedure TIWForm1.IWAppFormCreate(Sender: TObject);
var l_row_index: Integer;
l_c_do_modify_button: tIwButton;
l_column_index: Integer;
l_c_iwedit: tIwEdit;

begin
SetLength(m_c_do_modify_button_list, IwDbGrid1.RowLimit);
for l_row_index:= 0 to IwDbGrid1.RowLimit- 1 do
begin
l_c_do_modify_button:= tIwButton.Create(Self);
with l_c_do_modify_button do
begin
Caption:= 'Modify_'+ IntToStr(l_row_index);
// -- offset by 1 because of the row header
Tag:= l_row_index+ 1;
OnClick:= handle_do_modify_click;
end; // with l_iwbutton
m_c_do_modify_button_list[l_row_index]:= l_c_do_modify_but
ton;
end; // for l_row_index
SetLength(m_c_edit_list, k_table_column_count);
for l_column_index:= 0 to k_table_column_count- 1 do
begin
l_c_iwedit:= tIwEdit.Create(Self);
with l_c_iwedit do
Text:= 'Edit_'+ IntToStr(l_column_index);
m_c_edit_list[l_column_index]:= l_c_iwedit;
end; // for l_column_index
m_c_do_post_button:= tIwButton.Create(Self);
with m_c_do_post_button do
begin
Color:= clWebLime;
Caption:= 'Post';
OnClick:= handle_do_post_click;
Visible:= False;
end; // with m_c_do_post_button
m_c_do_cancel_button:= tIwButton.Create(Self);
with m_c_do_cancel_button do
begin
Color:= clWebRed;
Caption:= 'Cancel';
OnClick:= handle_do_cancel_click;
Visible:= False;
end; // with m_c_do_cancel_button
end; // IWAppFormCreate

Notez que chaque bouton de modification contient dans tIwButton.Tag le numro de la


ligne. Ceci sera utilis pour retrouver la ligne courante modifier lorsque le bouton sera
cliqu.
slectionnez la tIwDbGrid, crez son vnement OnRenderCell, et affichez, en fonction
de l'tat de la dbGrid, soit les boutons pour modifier, soit les Edit et les boutons
pour Poster. Et ces dernier uniquement sur la ligne courante. En gros, il y a deux tats:
affichage et modification, et en fonction du Booleanm_do_modify, nous affichons soit les
bouton de modification, soit les Edits et les boutons d'annulation/post
procedure TIWForm1.IWDBGrid1RenderCell(ACell: TIWGridCell
; const ARow,
AColumn: Integer);
procedure draw_in_display_mode;
var l_field_value: String;
begin
if ARow= 0
then // column header
else
case AColumn of
0 : begin
// -- the button column
ACell.Control:= m_c_do_modify_button_list[ARow- 1];
ACell.Control.Enabled:= True;
end;
1 : begin
l_field_value:= UserSession.AdoDataset1.Fields[0].AsSt
ring;
ACell.Text:= l_field_value;
end;
2 : begin
l_field_value:=
UserSession.AdoDataset1.FieldByName('Population').
AsString;
ACell.Text:= l_field_value;
end;
end; // case AColumn
end; // draw_in_display_mode
procedure draw_in_edit_mode;
procedure display_selected_row;
var l_field_value: String;
begin
case AColumn of
1 : begin
l_field_value:= UserSession.AdoDataset1.Fields[0].AsStr

ing;
ACell.Control:= m_c_edit_list[0];
m_c_edit_list[0].Text:= l_field_value;
m_selected_key:= l_field_value;
end;
2 : begin
l_field_value:= UserSession.AdoDataset1.FieldByName('
Population').AsString;
ACell.Control:= m_c_edit_list[1];
m_c_edit_list[1].Text:= l_field_value;
end;
3 : begin
ACell.Control:= m_c_do_post_button;
end;
4 : begin
ACell.Control:= m_c_do_cancel_button;
end;
end; // case AColumn
end; // display_selected_row
procedure display_other_row;
var l_field_value: String;
begin
case AColumn of
1 : begin
l_field_value:= UserSession.AdoDataset1.Fields[0].AsStr
ing;
ACell.Text:= l_field_value;
end;
2 : begin
l_field_value:=
UserSession.AdoDataset1.FieldByName('Population').
AsString;
ACell.Text:= l_field_value;
end;
end; // case AColumn
end; // display_other_row
begin // draw_in_edit_mode
if ARow= 0
then // column header
else
if aColumn= 0
then begin
// -- the modify column
ACell.Control:= m_c_do_modify_button_list[ARow- 1];
ACell.Control.Enabled:= False;
end

else
if ARow= m_selected_row
then display_selected_row
else display_other_row;
end; // draw_in_edit_mode
begin // IWDBGrid1RenderCell
if m_do_modify
then draw_in_edit_mode
else draw_in_display_mode;
end; // IWDBGrid1RenderCell
crez l'vnement qui traitera la mise en mode edition:
procedure TIWForm1.handle_do_modify_click(Sender: tObject);
begin
with (Sender as tIwButton) do
begin
m_do_modify:= True;
m_selected_row:= Tag;
m_c_do_post_button.Visible:= True;
m_c_do_cancel_button.Visible:= True;
end; // with Sender as tIwButton
end; // handle_do_modify_click

et les vnements qui traitent Cancel:


procedure TIWForm1.handle_do_cancel_click(Sender: TObject);
begin
// -- simply revert to display mode
m_do_modify:= False;
// -- reset the browse mode
m_c_do_post_button.Visible:= False;
m_c_do_cancel_button.Visible:= False;
end; // handle_do_cancel_click

et finalement la mise jour ( modifier si vous n'utilisez pas ADO):


procedure TIWForm1.handle_do_post_click(Sender: TObject);
var l_population: String;

begin
clear_display;
display('post '+ m_selected_key);
// -- use the data access controls to update the Table
l_population:= m_c_edit_list[1].Text;
with UserSession.AdoCommand1 do
begin
CommandText:=
'UPDATE country '
+ ' SET population= '+ l_population
+ ' WHERE Name= '''+ m_selected_key+ '''';
// display('cmd '+ CommandText);
Execute;
end; // with UserSession.AdoCommand1
(*
with UserSession.AdoDataset2 do
begin
Open;
if Locate('Name', m_selected_key, [])
then begin
Edit;
FieldByName('Population').AsString:= l_population;
Post;
end;
Close;
end; // with UserSession.AdoDataset2
*)
// -- refresh AdoDataset1
UserSession.AdoDataset1.Close;
UserSession.AdoDataset1.Open;
// -- reset the browse mode
m_do_modify:= False;
m_c_do_post_button.Visible:= False;
m_c_do_cancel_button.Visible:= False;
end; // handle_do_post_click

Nous avons laiss en commentaire la modification via AdoDataset2


compilez et excutez
voici la grille en mode affichage:

cliquez sur le second tButton:


voici la grille en mode modification:

Notez
tout d'abord nous nous sommes entts vouloir utiliser ADO. Lorsque nous
avons essay de mettre jour, nous avons eu une exception "OleException,
missing required parameter". Aprs quelques heures d'essai, nous avons
dcid d'utiliser un tAdoDataSet, et Locate n'a pas fonctionn, car la colonne
COUNTRY n'existait pas. Nous avions confondu
EMPLOYEE_GDB.COUNTRY et DBDEMOS_MDB.COUNTRY, et le
nom correct pour la colonne contenant le nom du pays est ici NAME.
"OleException, missing parameter" tait naturellement le message le plus
appropri pour nous mettre sur la voie. Pas aussi inutile que "Syntax Error",
mais pas loin, voire pire ...
revenons notre sujet. Nous aurions pu, comme ASP.NET utiliser la mme
colonne pour les boutons de modification et ceux de confirmation ou
annulation. Nous avons tout laiss pour mieux montrer ce qui se passait

nous avons cr des listes de boutons. Il faudrait naturellement les librer, et


ceci dans tIwForm.OnDestroy
nous avons utilis une grille de taille fixe pour pouvoir plus facilement
allouer nos tables de boutons. Il serait possible de parcourir la Table pour
allouer un nombre variable en fonction de la taille de la table
en plus des dbGrid, il existe aussi une grille tIwGrid (non lie une base de
donnes) que nous pouvons remplir, modifier etc
il arrive que lorsque nous crons des tIwButton ou autre contrle par code,
nous ne connaissions pas l'UNIT Intraweb importer. Nous pouvons
naturellement recherche cette unit en utilisant l'aide. Une solution bien
connue est de dposer un exemplaire de ce type de contrle via la Palette, de
compiler (ce qui forceDelphi ajouter le bon USES, puis retirer ce
composant
SELF est impratif dans tIwButton.Create(SELF) (sinon le clic ne sera pas
pris en compte)
peut-tre les nouvelles versions d'Intraweb faciliteront la modification d'une
grille, mais les messages trouvs sur les forums n'en parlent pas
la stratgie utilise pour modifier une grille est similaire celle que nous
avons utilise pour prsenter la navigation dans une grille en utilisant CGI, et
est la mme que celle utilise, nous l'avons dj indiqu, par ASP.NET
nous pourrions utiliser la mme technique pour nous positionner sur une
ligne en utilisant la grille
il faudrait aussi grer l'inhibition de previous et next pendant la modification
la dernire image ci-dessus prsente aussi un tIwMemo. Naturellement, nous
n'avons pu nous empcher d'afficher diffrents message de mise au point,
comme sous Windows. Ici le memo affiche la requte qui va tre excute.
Les mthodes pour afficher et effacer le memo sont dans les sources du .ZIP
en ce qui concerne la tIwDbGrid:
o UseFrame permet de supprimer le titre de
la tIwDbGrid. UseSize affiche un cadre autour de la grille
o tIwDbGrid.Colums[0] n'est pas accept. Il faut
utiliser tIwDbGrid.Colums[0].Items[0]

o apparemment, le titre des colonnes est compt comme une ligne de


la tIwDbGrid
o les tIwDbGridColumn ont aussi un vnement OnClick que nous
aurions peut tre pu utiliser au lieu de mettre en place des
boutons Modify
o CellPadding est le gras dans une cellule, CellSpacing la taille de la
bordure (comme pour les <TABLE>s .HTML, naturellement
o BgColor et la couleur de la zone dessine
o Options.dgShowTitles permet de supprimer les titres des colonnes

4.5 - Gestion des Sessions


Par dfinition, le protocole .HTTP est sans tat: lorsqu'un Client demande une page,
le Serveur la lui envoie, et oublie tout de cette requte. Ceci vite d'avoir grer au
niveau du Serveur l'tat de chaque Client.
Certaines applications ncessitent toutefois une gestion de la session. Si nous
souhaitons afficher les lignes d'une Table par groupe de 10 lignes, il faut bien
mmoriser quelque part que nous avons affich les lignes 09, et lorsque nous
cliquons "Next", nous souhaitons les lignes 1011. Il faut donc que notre application
(et non pas leServeur) se charge de grer l'tat du dialogue entre le Serveur et
le Client.

Les sessions sont gres automatiquement par Intraweb. En fait, c'est


la tIwApplication qui gre la session:
nous lanons le Serveur Intraweb
un nouvel utilisateur se connecte
un objet tIwApplication est automatiquement cr. Cet objet

contient la pile des tIwForm cres (les pages)

il contient un attribut tIwApplication.Data qui, par dfaut, pointe vers


un tIwUserSession qui est un descendant de tDataModule

et chaque tIwForm contient une rfrence tIwForm.WebApplication, vers l'application.