Académique Documents
Professionnel Documents
Culture Documents
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).
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:
"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
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.
Dans cet article, nous utiliserons les Serveur HTTP de dbugging intgr Delphi,
WAD.
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
tapez "Yes"
Delphi nous demande le nom de notre Service
nous suggrons d'appeler l'INTERFACE, et par consquent vous pouvez taper
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
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)
L'action qui provoque les calculs (et pas la fourniture du fichier .WSDL) est
implicite (pas affiche dans l'diteur d'actions).
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.
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:
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)
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:
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
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:
Notez que
vous pourriez aussi ajouter la nouvelle application au groupe par "Project
Manager | ProjectGroup1 | Add New Project etc
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
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
Notez que
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:
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
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
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 *
:
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.
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
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
cliquez "Dbloquer"
le Serveur IntraWeb est affich
IntToStr(StrToInt(IwEdit1.Text)* StrToInt(IwEdit2.Text));
end; // IWButton1Click
compilez
le Serveur Internet Intraweb est affich:
soit en tapant F9
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
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:
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.
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:
au moins un bouton
<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
le Serveur
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
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.
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.
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")
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
cliquez "backbutton"
la premire page est affiche nouveau:
Notez que
l'URL affiche un nombre d'aller et retour croissant: 0, 1, 2
et par la suite, cet utilisateur ajoute ou retire des pages actives sa liste par Show()
et Hide().
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)
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
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
dans l'Inspecteur
o initialisez DataBaseName avec DBDEMOS
o initialisez TableName avec ANIMALS.DBF
o basculez Active sur True
slectionnez la forme
slectionnez de l'onglet "IwData" de la Palette, et dans cette page
slectionnez un tiwDbEdit:
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.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
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.
slectionnez SERVERCONTROLLER.PAS
dans l'Inspecteur,
initialisez IwServerController.ComInitialization ciNormal ou ciMultiThreaded
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.
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
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:
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
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
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
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