Vous êtes sur la page 1sur 48

REST et Delphi 2010

Un livre blanc de Marco Cant (http://blog.marcocantu.com)

Novembre 2009

Corporate Headquarters EMEA Headquarters Asia-Pacific Headquarters


100 California Street, 12th Floor York House L7. 313 La Trobe Street
San Francisco, California 94111 18 York Road Melbourne VIC 3000
Maidenhead, Berkshire Australia
SL6 1SF, United Kingdom

Copyright 2009 Marco Cantu. All Rights Reserved.


SYNTHSE EXCUTIVE
La nouvelle architecture REST ( Representational State Transfer ) a un impact significatif
sur l'ensemble de lindustrie et la plupart des nouveaux services Web grand-public
conus par les acteurs majeurs du secteur (Google, Yahoo, Amazon et aussi Microsoft)
utilisent cette technologie pour partager et fusionner des informations multisources.
Limplmentation de larchitecture REST ne sappuyant que sur des technologies simples
(HTTP et XML), Delphi lui a toujours offert un support de choix ; sa dernire version
(Delphi 2010) le complte dune prise en charge spcifique pour le dveloppement de
serveurs REST dans linfrastructure DataSnap.
Cette tude revient sur les technologies REST impliques dans une perspective Delphi et
dmontre comment crer des applications clientes pour des sites Web populaire et comment
construire des serveurs REST avec le support spcifique offert par Delphi 2010.

INTRODUCTION
La dernire dcennie a vu lexplosion du Web et plus rcemment lapparition du Web 2.0.
Les interactions automatiques entre sites Web, sites et applications clientes ou entre sites
Web et bases de donnes mtier sont en revanche plus rcentes et exigent gnralement
des interconnexions globales parfois difficiles comprendre.
La vitesse de dplacement et de rafrachissement des donnes sur le Web est devenue bien
suprieure la capacit de consultation de chacun ; do une forte demande pour les
programmes permettant de localiser, suivre et superviser des informations multisources
(donnes commerciales, informations financires, communauts en ligne, campagnes
marketing, etc.).
POURQUOI DES SERVICES WEB ?
Lmergence des services Web permet denvisager de nouveaux modes dutilisation
dInternet par les entreprises... Naviguer sur diffrentes pages pour passer une commande
est suffisant pour les environnements B2C (business-to-consumer) mais certainement pas
pour les contextes B2B (business-to-business) plus sophistiqus. Par exemple, il est
gnralement trs ergonomique pour un utilisateur de rechercher et acheter des livres sur un
site marchand ; en revanche, si vous dirigez une librairie en ligne passant des centaines de
commandes par jour, la mme approche serait totalement inefficace surtout si dautres
applications dterminent les rapprovisionnements en fonction des ventes Ressaisir ces
donnes dans une autre application serait en effet compltement ridicule...
Les services Web ont (ou du moins avaient lorigine) prcisment pour vocation de
rsoudre ce problme. Par exemple, lorsque le programme cre automatiquement une
demande de rapprovisionnement de livres ; il ladresse ensuite un service Web qui
retourne immdiatement les informations sur la commande. Ltape suivante consistant par
exemple demander un numro de suivi de livraison. ce stade, le programme peut utiliser
un autre service Web pour suivre la livraison jusqu sa destination et informer le client sur
le temps dattente. la livraison, le programme peut galement adresser une information
(par SMS/Pager/Twitter) aux personnes ayant des commandes en attente, dclencher un
paiement avec un service Web bancaire, etc. Cet exemple donne une bonne ide de la
cinmatique gnrale : les services Web sont linteroprabilit des systmes ce que le
Web et le-mail sont aux interactions entre individus...
Le primtre des services Web est extrmement tendu et implique de multiples
technologies et standards. Je me concentrerai donc sur limplmentation Delphi sous-jacente
et sur les aspects techniques des services Web sans aborder leurs implications mtier au
sens large. Delphi for Win32 propose des solutions trs sophistiques pour prendre en
charge les services Web drives initialement de SOAP et qui peuvent aujourdhui tre
facilement tendues travers des composants HTTP et REST.
TECHNOLOGIES CLS DES SERVICES WEB : SOAP ET REST
Le concept de service Web est en soi plutt abstrait. Technologiquement, deux solutions
attirent principalement les dveloppeurs : le standard SOAP (Simple Object Access Protocol,
voir http://www.w3.org/TR/soap/) et REST (Representational State Transfer) avec sa
variante XML-RPC (XML-Remote Procedure Call).
Ces deux solutions utilisent gnralement HTTP comme protocole de transmission (bien quil
existe des alternatives) et XML (ou JSON) pour les dplacements bidirectionnels de
donnes. Le protocole HTTP standard permet au serveur Web de grer les requtes et de
faire transiter les paquets de donnes travers les pare-feux.
Ce livre blanc naborde pas les dtails de SOAP et porte exclusivement sur REST. Je
commencerai par dfinir les fondations thoriques, je prsenterai ensuite un exemple simple
de serveur et de client, je reviendrai en dtail sur le dveloppement de clients REST pour
des services Web REST particulirement populaires et je conclurai sur le support serveur
REST offert par Delphi 2010 en tant quextension de larchitecture DataSnap.

LES CONCEPTS SOUS-JACENTS DE


REST (REPRESENTATIONAL STATE
TRANSFER)
Mme si lide gnrale de REST est relativement ancienne, sa formalisation et la thorie
sous-jacente sont plus rcentes. Il convient avant tout de souligner quil nexiste pas de
standard REST formel.
REST est lacronyme de Representational State Transfer , initialement employ par Roy
Fielding dans son mmoire de Ph.D. de 2000, ce terme a t rapidement adopt pour
dsigner les accs aux donnes par le Web travers HTTP et des URL plutt quen
sappuyant sur le standard SOAP.
Initialement, le terme REST se rfrait un style architectural dcrivant la relation entre un
navigateur et un serveur Web. Lide tant que lorsquon accde une ressource Web (avec
un navigateur ou une application cliente spcifique) le serveur retourne une reprsentation
de ladite ressource (page, image, donnes brutes, etc.). Le client recevant cette
reprsentation est dfini dans un tat donn ; lorsquil accde dautres informations ou
pages (par exemple via un lien) son tat change lors du transfert.
Dans les propres mots de Roy Fielding : REST retranscrit la rationalit de comportement
dune application Web bien conue : un rseau de pages Web (ou machine dtat virtuelle)
o lutilisateur progresse travers lapplication en slectionnant des liens (transitions dtat)
pour afficher la page suivante (tat applicatif suivant) transfr et restitu lutilisateur pour
manipulation.
POINTS CLS DE LARCHITECTURE REST
Si REST est bien une architecture (ou mieux : un style architectural ), ce nest clairement
pas un standard (bien quil sappuie sur des standards existants : HTTP, URL, diffrents
types de formats pour les donnes, etc.). Alors que SOAP se superpose HTTP et XML ;
les architectures REST utilisent HTTP et XML (et dautres formats) exactement comme ils
sont :
REST utilise des URL pour identifier une ressource serveur (SOAP utilise une seule
URL pour plusieurs requtes, dtailles dans lenveloppe SOAP). On retiendra que
lURL identifie une ressource et non une opration sur la ressource.
REST recourt des mthodes HTTP pour indiquer les oprations effectuer
(rcuprer ou HTTP GET, crer ou HTTP PUT, mettre jour ou HTTP POST et
effacer ou HTTP DELETE)
REST utilise des paramtres HTTP (comme paramtres de requte et POST) pour
fournir des informations supplmentaires au serveur.
REST sappuie sur HTTP pour lauthentification, le cryptage et la scurit (avec
HTTPS)
REST retourne les donnes sous forme de documents bruts en diffrents formats
Mime (XML, JSON, images, etc.)
Dans ce type de scnario de nombreux lments architecturaux doivent tre pris en
considration. REST exige que les systmes soient :
Client/serveur par nature (pas de SGBDR ce niveau)
Sans tat
Adapts la gestion en cache (la mme URL doit retourner les mmes donnes si
elle est appele deux fois en squence sauf si les donnes ont chang ct
serveur), afin de permettre linsertion de serveurs de proxy et de cache entre le client
et le serveur. Le corollaire est que les oprations GET ne doivent pas avoir deffets
induits.
La thorie de REST est naturellement plus complte que la prsentation prcdente qui
constitue cependant un bon point de dpart. Les exemples pratiques qui suivent sont
accompagns du code Delphi et devraient clarifier ces concepts lmentaires.

DELPHI ET LES TECHNOLOGIES REST


Comme nous lavons vu, il nexiste pas de standard REST et le dveloppement REST
nexige aucun outil spcifique ; cependant, quelques standards connexes mritent une brve
description (sachant que chacun dentre eux pourrait faire lobjet une tude part entire).
Une fois encore, nous envisagerons ces technologies sous langle de la prise en charge par
Delphi.
HTTP (CLIENT ET SERVEUR)
Le protocole HTTP (HyperText Transfer Protocol) est au cur du Web et ne ncessite pas
une plus ample prsentation ; notons simplement quil peut tre utilis avec des navigateurs
mais aussi avec nimporte quelle autre application.
Avec Delphi, le moyen le plus simple dcrire une application cliente utilisant HTTP consiste
rpondre sur le composant client IdHttp (ou Indy HTTP). En appelant la mthode GET de
ce composant en fournissant une URL comme paramtre, il est possible de rcuprer le
contenu de nimporte quelle page Web et de nombreux serveurs REST. Il est parfois
ncessaire de dfinir dautres proprits pour fournir des informations dauthentification ou
attacher un deuxime composant pour le support SSL (nous le verrons par des exemples).
Ce composant prend alors en charge les diffrentes mthodes HTTP au-del de GET.
Notons que pour des raisons de scurit, les requtes IdHttp doivent tre ralises dans un
thread dans la mesure o la suite Indy utilise des threads bloquants pouvant geler linterface
jusquau retour des requtes (ce qui peut tre long si le serveur Web est lent ou en cas de
transferts volumineux). Dans les dmonstrations de cette tude, nous nutiliserons pas de
threads pour des raisons de simplicit mais cest bien lapproche recommande.
Du ct du serveur, il est possible dutiliser de multiples architectures pour crer un serveur
Web ou une extension de serveur Web dans Delphi. Pour un serveur Web autonome, il est
possible dutiliser le composant IdHttpServer et pour crer des extensions de serveur Web
(applications CGI, ISAPI ou modules Apache) le framework WebBroker. Une autre option est
possible grce au support HTTP de DataSnap dans Delphi 2010.
XML
Le langage XML (Extended Markup Language) est un format commun de donnes ; de
nombreux serveurs REST utilisent pourtant dautres structures comme JSON (JavaScript
Object Notation) et parfois mme des fichiers en texte brut, dlimits par des virgules. Une
fois encore, la technologie XML est bien connue et je ny reviendrai pas en dtail.
Delphi permet de traiter les documents XML avec le composant XmlDocument. Il sagit dun
encapsuleur destin lun des moteurs XML DOM disponibles (par dfaut Microsoft XML
DOM). Lorsquun document est charg, il est possible de naviguer travers les nuds de sa
structure ou deffectuer des requtes avec XPath (ma mthode prfre).
XPATH
XPath est un langage de requte pour identifier et traiter les nuds dun document XML. La
notation utilise par XPath ressemble un chemin daccs simple (/racine/nud1/nud2)
avec des crochets pour exprimer des conditions sur les attributs des nuds ou les sous-
nuds (racine/nud[@val=5]) ou mme des expressions complexes. Le rsultat dune
dclaration XPath peut tre en soi une expression (comme le nombre de nuds rpondant
la rgle ou la valeur totale dun ensemble de nuds).
Delphi permet dexcuter une requte XPath en lappliquant au DOM hbergeant le
document (si le DOM spcifique le prend en charge). Nous verrons un exemple dutilisation
dXPath dans la premire dmonstration ci-dessous.

CLIENTS REST CRITS EN DELPHI


Le Web propose une quantit impressionnante dexemples de serveurs REST (dont les
fameux Amazon Web Service, devenus Amazon E-Commerce Service depuis
quAmazon Web Service est utilis pour loffre de Cloud Computing) permettant daccder
aux informations en utilisant une structure de donnes XML plutt que le format HTML.
Mme si beaucoup de services Web utilisent REST sur Internet, la plupart des services Web
exigent du dveloppement (comme indiqu dans les dmos suivantes) et trs peu offrent un
vritable accs gratuit et ouvert. Pour une liste lgrement diffrente des clients REST
Delphi et pour obtenir le code source de ces dmonstrations, vous pouvez vous reporter aux
sections spcifiques de lun de mes sites Web :
http://ajax.marcocantu.com/delphirest/default.htm

CLIENT REST POUR LES FLUX RSS


Les formats les plus diffuss pour distribuer des informations XML sont RSS et ATOM ; ces
technologies sont plutt lies aux blogs et aux sites dinformation mais peuvent tre utilises
avec nimporte quelle source de donnes. Lintrt de ces technologies de diffusion rside
dans leur capacit fournir aux applications clientes les mmes informations que celles
auxquelles l'utilisateur accderait gnralement avec un navigateur Web. Les informations
de ces flux sont traites par les applications clientes et peuvent mme tre combines dans
une synthse de flux similaires (comme sur le site Delphi Feeds).
Pour le premier exemple dapplication cliente utilisant REST, jai crit un client RSS
extrmement simple recherchant dans les blogs Delphi sur http://www.delphifeeds.com.
Chaque fois que lon accde des donnes XML dynamiques avec une URL et que lon peut
modifier lURL pour accder des donnes diffrentes on utilise bien lapproche REST.
APPEL REST ET DOCUMENT XML
Le programme RssClient utilise un composant IdHttp et un composant XMLDocument.
Le premier est utilis pour rcuprer les donnes sur le Web (c'est--dire par appel client
REST) et les charger dans le second :
var
strXml: string;
begin
strXml := IdHTTP1.Get
('http://feeds.delphifeeds.com/delphifeeds');
XMLDocument1.LoadFromXML(strXml);

Les donnes extraites doivent ressembler ce qui suit (lgrement simplifi pour plus de
lisibilit) lorsquelles sont affiches dans un diteur XML :
TRAITEMENT DES DONNES RSS AVEC XPATH
Pour extraire les informations requises dans ce document XML, le programme RssClient
utilise des expressions XPath. Par exemple, pour lire le titre de la premire contribution du
blog (item) de la liste :

/rss/channel/item[1]/title

Cette opration est ralise en cycle, paralllement lextraction dautres informations


formates et affiches dans une liste droulante. Lutilisation de XPath ncessite une
interface spcifique pour le moteur Microsoft (lien avec linterface tendue
IDOMNodeSelect).
Ds quil dispose des nuds, le programme recherche tous les nuds texte enfants avec
une fonction daide getChildNodes cre cet effet et ajoute les donnes une zone de
liste. Ci-aprs le code intgral de la mthode excute en cliquant sur le bouton de mise
jour du programme ( Update ) :
procedure TRssForm.btnUpdateClick(Sender: TObject);
var
strXml, title, author, pubDate: string;
I: Integer;
IDomSel: IDOMNodeSelect;
Node: IDOMNode;
begin
strXml := IdHTTP1.Get
('http://feeds.delphifeeds.com/delphifeeds');

XMLDocument1.LoadFromXML(strXml);
XMLDocument1.Active := True;
IDomSel := (XMLDocument1.DocumentElement.DOMNode
as IDOMNodeSelect);
for I := 1 to 15 do
begin
Node := IDomSel.selectNode(
'/rss/channel/item[' + IntToStr (i) + ']/title');
title := getChildNodes (Node);
Node := IDomSel.selectNode(
'/rss/channel/item[' + IntToStr (i) + ']/author');
author := getChildNodes (Node);
Node := IDomSel.selectNode(
'/rss/channel/item[' + IntToStr (i) + ']/pubDate');
pubDate := getChildNodes (Node);
ListBox1.Items.Add(author + ': ' + title + ' [' +
pubDate + ']');
end;
end;
Lcran ci-dessous prsente les effets de lexcution de ce programme :
DES CARTES ET EMPLACEMENTS
Laccs des lments de cartographie et de positionnement est trs souvent ncessaire
pour toutes les applications incluant une ou plusieurs adresses ; de multiples informations
cartographiques sont accessibles sur diffrents sites majeurs (Google, Yahoo, Microsoft,
etc.).
SERVICE DE CODAGE GOGRAPHIQUE DE
GOOGLE
Nous commencerons par le service de gocodification de Google permettant de soumettre
une adresse et de rcuprer ses coordonnes de latitude/longitude partir dune requte
telle que :

http://maps.google.com/maps/geo?q=[address]&output=[format]
&key=[key]

Il est galement possible d'entrer une URL similaire dans son navigateur des fins de test
(voir ci-aprs les coordonnes de New York dans un navigateur en format XML) :

Mon exemple de golocalisation sappuie sur les adresses dentreprise de la base de


donnes exemple Customer.cds fournie avec Delphi (utilisant une copie locale, dans le
fichier ZIP avec le code source du projet).
Comme la plupart de ces services, lutilisation est gratuite pour un usage limit (le
programme intgre quelques appels sleep() supplmentaires pour ne pas atteindre le taux
maximal par minute) mais exige un enregistrement pour le service considr sur
http://code.google.com.
Le programme de dmonstration exige que vous ajoutiez votre devkey un fichier
GeoLocation.ini devant rsider dans le rpertoire des documents de lutilisateur ; sa structure
est simple :
[googlemap]
devkey=

RSOLUTION ADRESSE CLIENT


Le programme fonctionne en deux tapes :
Tout dabord, il recherche des noms uniques de villes/tats/pays en scannant le
composant ClientDataSet et en compltant une liste de chane. Ce code ntant pas
li REST ne figure pas dans ce document.
La deuxime tape consiste rechercher chaque ville avec le service Google
Geocoding et complter un composant mmoire ClientDataSet avec les
informations rsultantes.
Cette fois, plutt que de demander la version XML des donnes, nous recourons un format
CSV plus simple que le programme analyse avec un objet StringList.
Ci-dessous, le code Geocoding :
procedure TFormMap.btnGeocodingClick(Sender: TObject);
var
I: Integer;
strResponse, str1, str2: string;
sList:TStringList;
begin
cdsTown.Active := False;
cdsTown.CreateDataSet;
cdsTown.Active := True;
sList := TStringList.Create;

for I := 0 to sListCity.Count - 1 do
begin
ListBox1.ItemIndex := I;
if Length (sListCity.Names[I]) > 2 then
begin
strResponse := IdHTTP1.Get( TIDUri.UrlEncode(
'http://maps.google.com/maps/geo?q=' +
(sListCity.Names[I]) + '&output=csv&key=' +
googleMapKey));
sList.LineBreak := ',';
sList.Text := strResponse;
str1 := sList[2];
str2 := sList[3];
cdsTown.AppendRecord([sListCity.Names[I],
StrToFloat (str1), StrToFloat (str2),
Length (sListCity.ValueFromIndex[I])]);
Sleep (150);
Application.ProcessMessages;
end;
end;
sList.Free;
end;
Le rsultat doit ressembler la copie dcran qui suit :
YAHOO MAPS
Ltape suivante peut consister accder la vritable carte correspondant une adresse.
Google Maps propose dinnombrables fonctionnalits conues pour tre hberges sur les
sites Web plutt que dans les applications clientes. (Je propose nanmoins un exemple
dhbergement Google Map dans un programme client ; larchitecture et le code en sont
nanmoins complexes et ne sont pas prsents dans cette tude).
Ce nouvel exemple dnomm YahooMaps utilise lAPI Yahoo Map pour obtenir une carte et
lafficher dans un contrle Image. Les informations sur cette API REST et le lien pour obtenir
un identifiant gratuit pour Yahoo Application sont accessibles sur :
http://developer.yahoo.com/maps/
Une fois encore, cet identifiant est requis pour excuter les programmes et enregistrer un
fichier INI spcifique dans le rpertoire Documents utilisateur dnomm YahooMaps.ini.
La carte est rcupre en deux tapes : le premier appel HTTP passe ladresse et reoit
lURL de limage de la carte ; le deuxime appel HTTP la rcupre. Une fois encore, ces
deux tapes peuvent tre simules dans un navigateur Web trs utile pour le dbogage.
Le programme utilise la base de donnes et llment StringList de lexemple prcdent. Il
intgre galement un bouton permettant dafficher la carte ou une ville dfinie (par exemple,
San Jos, Californie) avec la mthode suivante :
const
BaseUrl = 'http://api.local.yahoo.com/MapsService/V1/';
procedure TFormMap.Button1Click(Sender: TObject);
var
strResult: string;
memStr: tFileStream;
begin
strResult := IdHTTP1.Get(BaseUrl +
'mapImage?' +
'appid=' + yahooAppid +
'&city=SanJose,California');
XMLDocument1.Active := False;
XMLDocument1.XML.Text := strResult;
XMLDocument1.Active := True;
strResult := XMLDocument1.DocumentElement.NodeValue;
XMLDocument1.Active := False;

// maintenant, nous rcuprons limage rfrence


memStr:= TFileStream.Create ('temp.png', fmCreate);
IdHttp1.Get(strResult, memStr);
memStr.Free;

// Chargement de limage
Image1.Picture.LoadFromFile('temp.png');
end;

La premire requte GET HTTP fournit la requte relle et retourne un document XML
avec lURL de limage de la carte comme suit (identifiant long omis) :

<Result>
http://gws.maps.yahoo.com/mapimage?MAPDATA=[...]&amp;mvt=m
&amp;cltype=onnetwork&amp;.intl=us&amp;appid=[...]
&amp;oper=&amp;_proxy=ydn,xml
</Result>
Cest la raison pour laquelle le programme peut extraire la valeur du seul nud avec le
code :
XMLDocument1.DocumentElement.NodeValue
Finalement, limage est enregistre dans un fichier temporaire et charg dans un contrle
Image. Au-del de la carte de cette ville spcifique, le programme peut galement retrouver
celles de la base de donnes Customer.cds de lexemple prcdent. Un bouton permet
dobtenir la carte de lemplacement spcifiquement indiqu (San Jos, exemple de la
dmonstration Delphi Live).
En voici un exemple :

API GOOGLE TRANSLATE


Google propose un autre exemple dAPI REST avec son service de traduction ( Google
Translate REST API dont la documentation est disponible sur
http//code.google.com/apis/ajaxlanguage/documentation/

Dans ce cas, il est inutile dobtenir une cl de signature (ni de fichier INI) ; il suffit de fournir
un site rfrent (bien que tout semble fonctionner mme sans cette information). Il est
possible de demander une traduction dans son navigateur Web en entrant une URL du type :
http://ajax.googleapis.com/ajax/services/language/translate?
v=1.0&q=What%20a%20nice%20day&langpair=en|de
Le rsultat de cet appel est prsent ci-dessous (avec rsultat JSON pour plus de lisibilit) :

{
"responseData":
{
"translatedText":"Was fr ein schner Tag"
},
"responseDetails": null,
"responseStatus": 200
}

Cet exemple accomplit une tape de plus par rapport aux prcdentes dmonstrations. Au
lieu dune requte HTTP, il utilise un composant VCL spcifique, appel par une mthode de
classe (pour viter de placer le composant dans un formulaire -- mme si cela est possible).
Ce composant de support rend lAPI extrmement simple demploi et encapsule
intgralement lappel HTTP.
COMPOSANT DE TRADUCTION
Ci-aprs la dclaration de classe du composant :
type
TBabelGoogleRest = class (TComponent)
protected
Http1: TIdHttp;
FFromLang: string;
FToLang: string;
FActiveInForm: Boolean;
procedure SetFromLang(const Value: string);
procedure SetToLang(const Value: string);
public
function DoTranslate (strIn: string): string; virtual;
constructor Create(AOwner: TComponent); override;

class function Translate (strIn, langIn, langOut:


string): string;
published
property FromLang: string read FFromLang write
SetFromLang;
property ToLang: string read FToLang write SetToLang;
end;

Le traitement effectif se situe dans la fonction DoTranslate :

function TBabelGoogleRest.DoTranslate(strIn: string): string;


var
strUrl, strResult: string;
nPosA, nPosB: Integer;
begin

strUrl := Format (
'http://ajax.googleapis.com/ajax/services/language/tran
slate?' +
'v=1.0&q=%s&langpair=%s',
[TIdUri.ParamsEncode (strIn),
FFromLang + '%7C' + FToLang]); // Format naime pas du
tout %7 !!!
strResult := Http1.Get(strUrl);

nPosA := Pos ('"translatedText":', strResult); // Dbut


des donnes JSON
if nPosA = 0 then
begin
nPosA := Pos ('"responseDetails":', strResult);
nPosA := nPosA + Length ('"responseDetails":');
end
else
nPosA := nPosA + Length ('"translatedText":');

nPosA := PosEx ('"', strResult, nPosA) + 1; // Guillemets


ouvrants
nPosB := PosEx ('"', strResult, nPosA) - 1; // Guillemets
fermants
Result := Copy (strResult, nPosA, nPosB - nPosA + 1);
end;
Le rsultat de la requte lURL indique est en format JSON (considr comme une API
Javascript par Google). Jai choisi de traiter manuellement linformation JSON mais jaurais
pu utiliser le nouveau support de mappage JSON vers objet de Delphi 2010 que nous
aborderons plus tard. La mthode effective peut tre appele par une mthode de classe
crant un objet temporaire, dfinissant ses proprits et appelant la fonction DoTranslate.
Dautres scnarii sont possibles mais le code restant doit rester simple comprendre.
Le formulaire principal du programme de dmonstration dispose dune bote de liste
proposant tous les langages supports.
Lexemple effectue une traduction partir de langlais mais peut tre naturellement invers.
Thoriquement, toute combinaison de deux langues devrait fonctionner ; en pratique ce nest
pas toujours le cas Lors de la demande dune traduction, les rsultats sont ajouts un
journal. Ici, lappel est appliqu au premier groupe de langues (par ordre alphabtique) :
TWITTER AUSSI SIMPLE QUE POSSIBLE...
Linterface de Twitter a largement contribu lessor de ce site et a donn naissance un
cosystme complet dapplications interfaces avec Twitter. Linterface de service Web
REST de Twitter est aussi simple que le service lest pour ses utilisateurs. Twitter a t
essentiellement dvelopp en Ruby et Ruby on Rails nest certainement pas tranger
lide de mise en avant de REST en raison de son mappage clair des URL avec les
ressources applicatives internes -- une des fondations de lapproche REST.
Si vous disposez dun compte Twitter vous pouvez accder vos 20 dernires entres et
aux 20 dernires entres gnrales avec les URL suivantes :
http://twitter.com/statuses/user_timeline.xml
http://twitter.com/statuses/public_timeline.xml

La seule condition est de raliser une requte GET HTTP passant len-tte HTTP standard
avec le nom utilisateur et le mot de passe une opration extrmement simple ajouter
dans Delphi aux proprits correspondantes dun composant IdHttp. Une opration plus
complique, si lon peut dire, consiste poster une mise jour dtat sur votre compte. Cette
opration est ralise avec une requte POST HTTP passant un paramtre dtat comme
dans le snippet qui suit, permettant dadresser Twitter le texte de lun des champs de
lenregistrement actuel dun composant ClientDataSet (ClientDataSet1TEXT) et de le
marquer comme post :
procedure TForm34.btnPostCurrentClick(Sender: TObject);
var
strResult: string;
listParams: TStringList;
begin
listParams := TStringList.Create;
listParams.Add('status=' + ClientDataSet1TEXT.AsString);
try
strResult := idhttp1.Post(
'http://twitter.com/statuses/update.xml',
listParams);
ShowMessage (strResult);
ClientDataSet1.Edit;
ClientDataSet1POSTED.AsString := 'T';
ClientDataSet1.Post;
finally
listParams.Free;
end;
end;
Ce code est extrait de mon application Delphi Tweet of the Day qui utilise un composant
ClientDataSet (local ou raccroch un serveur distant) pour saisir les Tweets. Aprs avoir
post la valeur du champ TEXT de la table, en ralit, le programme indique Vrai dans le
champ POSTED.
Il y a dautre code dans le programme mais rien qui soit rellement li Twitter ni son API
REST. Llment suivant pertinent est la configuration du composant IdHttp :
object IdHTTP1: TIdHTTP
Request.ContentLength = -1
Request.Accept = 'text/html, */*'
Request.BasicAuthentication = True
Request.Password = '***' // Omis mais requis
Request.UserAgent = 'tweetoftheday'
Request.Username = 'delphitweetday'
HTTPOptions = [hoForceEncodeParams]
end
En dautres termes, le codage Twitter est extrmement simple et permet de bnficier dune
mise en ligne automatique intervalles fixes ou en cas de survenance dun vnement
pertinent pour vos activits (et dans votre base de donnes).

INTERFAAGE AVEC LES SERVICES GOOGLE


SPREADSHEET
Si lintgration de cartes, de traductions et de communications Twitter peut devenir partie
intgrante de votre marketing dentreprise, il deviendra de plus en plus important au fil du
temps de pouvoir interagir avec des composants mtier tels que Google Docs
(http://docs.google.com/) dont linterface de Service Web permet de tlcharger et rcuprer
(voire mme convertir) des documents Web et de contrler leurs habilitations daccs. Des
fonctionnalits utiles qui peuvent tre compltes grce linterface permettant de travailler
avec le contenu dun document Web : lAPI Google Spreadsheet Services (encore en phase
bta lors de la rdaction de ce document).
Cette API permet daccder chaque cellule de nimporte quelle feuille de calcul du systme
(prive ou publique). Toute personne recherchant ces documents avec une interface Web
peut ainsi consulter les changements en temps rel ! Dans la mesure o cette API (comme
beaucoup dautres API Google) permet daccder aux documents personnels et rservs,
elle exige une couche de scurit plus robuste que les API REST utilises jusqu prsent.
Comparativement des solutions plus simples, il existe deux diffrences notables :
LAPI utilise HTTPS plutt que HTTP. Il est donc ncessaire de rfrencer OpenSSL ou une
autre bibliothque SSL lapplication cliente et dappeler le code de support Indy adapt.
LAPI exige une requte dauthentification spare, qui retournera un jeton dhabilitation
dure limite. Ce jeton devra ensuite tre inclus dans toutes les requtes ultrieures
(manant de la mme adresse IP).
Avant denvisager le problme spcifique qui nous proccupe, il est ncessaire dexaminer
cette infrastructure (en gardant lesprit que ces classes de gestion des habilitations ont t
tablies pour une autre catgorie de Services Web Google, linterface Provisioning API,
utilise pour crer des comptes dans des domaines Google privs et payants de messagerie
et dchange documentaire).
Pour supporter lauthentification et mettre une requte partir de ce support, jai cr une
classe Delphi spcifique, TGoogleAuth, qui dispose de linterface publique suivante (les
multiples mthodes prives daccs aux champs et proprits ont t supprimes) :
type
TGoogleAuth = class
public
property GoogleEmail: string
read FGoogleEmail write SetGoogleEmail;
property GooglePwd: string
read FGooglePwd write SetGooglePwd;

property AuthString: string


read GetAuthString write SetAuthString;
property ReqTime: TDateTime
read FReqTime write SetReqTime;
property AccountType: string
read FAccountType write SetAccountType;
property ServiceName: string
read FServiceName write SetServiceName;
public
function Expired: Boolean;
procedure Renew;
end;
Quatre de ces proprits (e-mail, mot de passe, type de compte et nom de service) sont des
valeurs dentre les deux autres correspondant la chane dhabilitation et sa date de
cration, utilises pour permettre son renouvellement automatique un certain nombre de fois.
Lorsque ce jeton est demand, la classe vrifie sil est ncessaire den obtenir un nouveau. Il
sagit de la mthode getter de la proprit AuthString :
function TGoogleAuth.GetAuthString: string;
begin
if FAuthString = '' then
Renew;
if Expired then
Renew;
Result := FAuthString;
end;
Le code de requte dhabilitation est intgr la mthode Renew , qui utilise une
connexion SSL HTTP pour solliciter le jeton d'habilitation transmettant le nom d'utilisateur et
mot de passe en format crypt :
procedure TGoogleAuth.Renew;
var
res, req: String;
sList: TStringList;
IdHttp: TIdHttp;
begin
FAuthString := '';

IdHttp := TIdHttp.Create (nil);


try
IdHttp.IOHandler :=
TIdSSLIOHandlerSocketOpenSSL.Create(IdHttp);
req :=
'https://www.google.com/accounts/ClientLogin?Email=' +
FGoogleEmail + '&Passwd=' + FGooglePwd +
'&accountType=' + FAccountType + '&service=' +
FServiceName;

res := IdHttp.Get (req);


finally
idHttp.Free;
end;

sList := TStringList.Create;
try
sList.Text := res;
FAuthString := sList.Values['Auth'];
FReqTime := Now;
finally
sList.Free;
end;
end;
Jai cr un service global de type singleton pour cette classe ; chaque fois quune
requte est ncessaire, japplique une mthode helper (fonction globale), qui ajoute le
jeton supplmentaire. Voici le code de cette (assez longue) fonction :
function DoRequest (
const methodAttr, fromAttr, strData: string): string;
var
res: String;
postStream: TStream;
IdHttp: TIdHttp;
resStream: TStringStream;
begin
IdHttp := TIdHttp.Create (nil);
try
// Ajot autorisation de la cl enregistre
IdHttp.Request.CustomHeaders.Values ['Authorization'] :=
'GoogleLogin auth=' + googleAuth.AuthString;
IdHttp.Request.CustomHeaders.Values ['Content-type'] :=
'application/atom+xml';
IdHttp.Request.CustomHeaders.Values ['GData-Version'] :=
'2';
// Utilisation SSL
IdHttp.IOHandler :=
TIdSSLIOHandlerSocketOpenSSL.Create(IdHttp);
try
if (methodAttr = 'post') or (methodAttr = 'put') then
begin
postStream := TStringStream.Create (strData);
try
postStream.Position := 0;
if (methodAttr = 'post') then

res := IdHttp.Post (fromAttr, postStream)


else // (methodAttr = 'put')
res := IdHttp.Put (fromAttr, postStream);
finally
PostStream.Free;
end;
end
else if (methodAttr = 'delete') then
begin
resStream := TStringStream.Create ('');
try
IdHttp.DoRequest (hmDelete, fromAttr, nil,
resStream);
finally
resStream.Position := 0;
res := resStream.DataString;
resStream.Free;
end;
end
else // 'get' ou non affect
res := IdHttp.Get (fromAttr);
except
on E: Exception do // Interception et utilisation comme
rsultat
begin
res := E.Message;
end;
end;
finally
IdHttp.Free;
end;
Result := res;

Bien quun peu lourd, ce code structurel est prcieux pour simplifier les appels SSL habilits.
Ainsi, pour requrir une liste des feuilles de calcul prives, on utilisera le code suivant :
DoAppRequest (
'get',
'http://spreadsheets.google.com/feeds/spreadsheets/private
/full',
'');
Ce code fait partie de la dmonstration, qui utilise les informations disponibles dans le fichier
dbtosheet.ini structur comme suit et charg dexcuter le programme de dmo :
[google]
email=
password=
accounttype=GOOGLE
Le fichier est charg au dmarrage du programme et les trois valeurs de larborescence sont
utilises pour renseigner l'objet TGoogleAuth, dsign googleAuth. Le quatrime paramtre
le service est dfini par le programme :
googleAuth.GoogleEmail :=
inifile.ReadString('google', 'email', '');
googleAuth.GooglePwd :=
inifile.ReadString('google', 'password', '');
googleAuth.AccountType :=
inifile.ReadString('google', 'accounttype', 'GOOGLE');
googleAuth.ServiceName := 'wise';
Dans cette configuration, le programme dispose dun premier bouton permettant de requrir
la liste de feuilles de calcul disponibles et d'ajouter leurs identifiants (ou URL) dans une bote
liste. Lobjet XML en rsultant est analys avec XPath aprs limination de lespace de
nommage, qui ne doit pas tre pris en compte par les requtes XPath, au risque de devenir
excessivement complexes :
procedure TForm34.btnListSheetsClick(Sender: TObject);
var
strXml: string;
IDomSel: IDOMNodeSelect;
Node: IDOMNode;
I, nCount: Integer;
title, id: string;
begin
strXml := DoAppRequest ('get',
'http://spreadsheets.google.com/feeds/spreadsheets/priv
ate/full',
'');
strXml := StringReplace (strXml,
'<feed xmlns=''http://www.w3.org/2005/Atom''', '<feed
', []);
XMLDocument1.LoadFromXML(strXml);
XMLDocument1.Active := True;

IDomSel := (XMLDocument1.DocumentElement.DOMNode as
IDOMNodeSelect);
nCount := IDomSel.selectNodes ('/feed/entry').length;
for I := 1 to nCount do
begin
Node := IDomSel.selectNode(
'/feed/entry[' + IntToStr (i) + ']/title');
title := getChildNodes (Node);

Node := IDomSel.selectNode(
'/feed/entry[' + IntToStr (i) + ']/content/@src');
id := getChildNodes (Node);
ListBox1.Items.Add(title + '=' + id);
end;
end;
La boucle sexcute sur chaque nud (en utilisant la fonction de dcompte XPath pour
dterminer le nombre de nuds) et ajoute le titre de la feuille de calcul et son ID/URL la
liste, comme le montre lillustration suivante :

Grce ces donnes didentification ID/URL, il est possible dadresser une seconde requte
pour demander les feuilles donglet disponibles dans le document spcifique. Mme sil nen
existe quun seul, il est ncessaire de se rfrer un onglet spcifique dans chaque
opration affectant le contenu du document. Ceci permet au programme de rcuprer
lidentifiant dans la liste et dtablir la seconde requte REST :
strSheetId := ListBox1.Items.ValueFromIndex
[ListBox1.ItemIndex];
strXml := DoAppRequest ('get', strSheetId, '');

ce stade, une autre boucle similaire permet dextraire les noms de feuilles et de les
dplacer dans une seconde bote liste :
IDomSel := (XMLDocument1.DocumentElement.DOMNode as
IDOMNodeSelect);
nCount := IDomSel.selectNodes ('/feed/entry').length;
for I := 1 to nCount do
begin
Node := IDomSel.selectNode(
'/feed/entry[' + IntToStr (i) + ']/title');
title := getChildNodes (Node);
Node := IDomSel.selectNode(
'/feed/entry[' + IntToStr (i) + ']/content/@src');
id := getChildNodes (Node);
ListBox2.Items.Add(title + '=' + id);
end;

Mais la vritable force de ce programme est sa capacit ajouter des donnes au document
lui-mme. Le moyen le plus simple dy parvenir est de crer un document se comportant
comme une table de base de donnes (cest--dire disposant dune premire ligne avec les
noms de champs). Ce type de document, apparaissant dans le navigateur, est illustr ci-
dessous :
Les cellules de la premire ligne sont de simples entres texte. Lintrt est qu'il permet de
se rfrer aux colonnes correspondantes l'aide d'un espace de nommage dynamique
comme :

<gsx:Company>Tom Sawyer Diving Centre<(gsx:Company>

Ainsi, il est possible dajouter de nouvelles lignes au document en rcuprant les champs
dune table avec les noms correspondants. Ceci ncessite une requte POST HTTP
reposant sur lidentifiant ID :

const
gsxNameSpace =
'xmlns:gsx="http://schemas.google.com/' +
'spreadsheets/2006/extended"';

begin
strSheetId := ListBox2.Items.ValueFromIndex
[ListBox2.ItemIndex];

Memo1.Lines.Add(
DoAppRequest ('post',
strSheetId,
'<entry xmlns="http://www.w3.org/2005/Atom" ' +
gsxNameSpace + '>' +
recordtoxml +
'</entry>'));

La fonction recordtoxml rcupre les valeurs des champs qui nous intressent dans
lenregistrement en cours du jeu de donnes client (ClientDataSet) utilis par le programme
et produit les donnes dentre appropries :
function recordtoxml: string;
begin

Result :=
FieldToXml ('custno') +
FieldToXml ('company') +
FieldToXml ('city') +
FieldToXml ('state') +
FieldToXml ('country');
end;

function FieldToXml (fieldname: string): string;


begin
Result := '<gsx:' + fieldname + '>' +
ClientDataSet1.FieldByName(fieldname).AsString +
'</gsx:' + fieldname + '>';
end;

Comme prcdemment mentionn, lespace de nommage gsx: pseudo se rfre aux noms
des colonnes de la feuille de calcul, qui sont dtermins par les chanes de la premire ligne.
Ce code permet dajouter la feuille de calcul de nouvelles lignes correspondant des
informations enregistres dans la base de donnes.
Cependant, rien ne vous empche (ni toute autre personne disposant de droits ddition sur
le document) dditer les donnes aprs leur publication ; le document recalculera alors
automatiquement les totaux en fonction des donnes adresses par lapplication cliente
permettant n'importe quel utilisateur correctement habilit de consulter le document
rsultant.
Nous avons donc cr un mcanisme sophistiqu de consultation et d'dition de donnes
manant de tables de base de donnes, avec des mises jour automatiques et continues et
une solide infrastructure dautorisation et tout cela partir dun compte Gmail gratuit !

SERVEURS REST DANS DELPHI 2010


Aprs ces longs dveloppements dans lesquels nous avons analys une large gamme
dapplications REST clientes crites dans Delphi et appris rpondre diffrentes requtes
dautorisation et utiliser diffrents formats de type de donnes, il est temps daborder la
seconde partie de ce livre blanc, consacre au dveloppement de serveurs REST dans
Delphi 2010.
Il convient tout dabord de prciser que ceci est possible dans nimporte quelle version de
Delphi capable de crer un serveur Web cest--dire probablement partir de Delphi 3.
Aujourdhui, vous pouvez utiliser le composant IdHTTPServer ou larchitecture WebBroker
ou dautres solutions tierces pour dployer votre propre serveur REST personnalis. Cest ce
que jai moi-mme fait pendant des annes, comme le montrent les exemples cits dans
mon ouvrage Mastering Delphi 2005. Ce livre blanc se limitera donc dlibrment une
option spcifique, car celle-ci est troitement intgre dans Delphi 2010 et constituera
probablement la base des futures extensions produit fournies par Embarcadero
Technologies.
CONCEPTION DUN PREMIER SERVEUR REST SIMPLIFIE
Pour dvelopper un premier serveur REST simplifi dans Delphi 2010, il est possible
dutiliser lassistant DataSnap:
Comme le montre lillustration ci-dessus, il existe deux assistants DataSnap et il est donc
ncessaire den choisir un. Si vous souhaitez hberger votre serveur REST en tant que
serveur Web, il est probablement recommand de choisir Application WebBroker DataSnap;
vous pourrez toutefois ajouter ultrieurement des composants au projet gnr par les deux
assistants pour les rendre similaires. En rgle gnrale, il est recommand dutiliser un
serveur DataSnap ordinaire si vous envisagez de dployer des clients Delphi ou d'autres
applications et si votre serveur se trouve sur votre rseau interne, tandis qu'un serveur
DataSnap WebBroker intgr au serveur HTTP sera prfrable pour les architectures
ouvertes et pour invoquer le serveur REST partir dapplications bases sur navigateur.
Dun point de vue lgrement diffrent, le serveur DataSnap WebBroker offre plus de
contrle sur les requtes HTTP entrantes et une fonctionnalit dintgration des donnes
REST dans le serveur WebBroker.
Si vous avez choisi cette option, une bote de dialogue saffichera avec quelques
paramtres. La principale dcision prendre dans cet assistant concerne le choix du
squelette applicatif.
Mais aucune inquitude Vous pourrez modifier larchitecture projet en prservant
ultrieurement lintgralit de votre code :

Pour un hbergement effectif sur serveur Web, l'option ISAPI est recommande bien que
loption CGI (gnralement plus lente) soit plus simple paramtrer et dboguer.
Toutefois, pour de relles fonctionnalits de dbogage et de test, il est fortement conseill
d'utiliser l'infrastructure Dbogueur dapplication Web fournie par Delphi, qui permet dajouter
directement un point de rupture au code et de contrler les flux de donnes HTTP (y compris
les en-ttes). Si vous choisissez cette option, il vous faudra insrer un nom de classe
interne, exclusivement utilis comme rfrence programme (dans lURL Dbogueur
dapplication Web). Notez galement que mme si cela nest pas explicitement mentionn
dans la bote de dialogue de lassistant, Delphi continue supporter les modules
dintgration de serveur Apache - tant prcis que le paramtrage projet sera manuel.
La bote de dialogue de lassistant DataSnap WebBroker permet de demander une classe de
mthode serveur prte lemploi avec des chantillons ; cependant, vous pouvez, une fois
encore, trs simplement dployer la vtre dans le code. Avec les paramtrages dcrits ci-
dessus, Delphi gnrera un projet avec diffrentes units organises en arborescence :
Un formulaire principal sans usage spcifique mais pouvant servir pour les
informations de connexion. Ce formulaire sera supprim lorsque vous modifierez la
structure projet pour un module serveur Web ou CGI ne vous en proccupez donc
pas trop.
Un module de donnes Web (hritier de TWebModule) hbergeant les composants
serveur DataSnap
Un module de donnes agissant comme une classe serveur, permettant d'ajouter le
code excuter dans le serveur REST.
MODULE DE DONNES WEB GNR PAR LASSISTANT
DATASNAP WEBBROKER
Nous examinerons tout dabord en dtail ces deux modules de donnes, avant d'ajouter de
nouvelles lignes de code programme et de les tester. Le module Web est un composant cl
de larchitecture WebBroker. Il est capable de dfinir de multiples actions et dispose
d'vnements pr- et post-traitement pour n'importe quelle requte HTTP. Sur ce module
Web, il est possible dajouter des composants qui interceptent des actions URL donnes,
comme DSHTTPWebDispatcher dans lexemple fourni ci-dessous :

object DSHTTPWebDispatcher1: TDSHTTPWebDispatcher


RESTContext = 'rest'
Server = DSServer1
DSHostname = 'localhost'
DSPort = 211
WebDispatch.MethodType = mtAny
WebDispatch.PathInfo = 'datasnap*'
end

Ce composant intercepte nimporte quelle requte avec une URL commenant par
datasnap , transmise au support HTTP de DataSnap. Pour les requtes commenant par
datasnap et indiquant un chemin daccs rest , le traitement sera redirig sur le
moteur REST intgr. En dautres termes, les requtes ayant un chemin d'accs
datasnap/rest sont considres comme des requtes REST.
Sagissant de deux chanes, il est possible de les modifier pour prendre en compte
diffrentes URL, comme il est expliqu plus loin dans ce document. Mais pour commencer,
nous examinerons les paramtrages standards.
Les deux autres composants du module de donnes Web constituent la base globale de
DataSnap et indiquent quelle classe rpondra aux requtes (ou quelles classes, en cas
dajout de composants DSServerClass multiples). Les paramtres par dfaut sont les
suivants :
object DSServer1: TDSServer
AutoStart = True
HideDSAdmin = False
end
object DSServerClass1: TDSServerClass
OnGetClass = DSServerClass1GetClass
Server = DSServer1
LifeCycle = 'Session'
end
Le composant DSServer ne demande qu tre dmarr (manuellement ou
automatiquement) ; en revanche, la configuration DSServerClass a essentiellement lieu dans
le gestionnaire dvnement retournant la classe cible. Le code par dfaut (gnr par
lassistant), qui retourne la classe du module de donnes secondaire (TServerMethods1)
hberg par lunit correspondante (Fsrs_ServerClass) est le suivant :
procedure TWebModule2.DSServerClass1GetClass(
DSServerClass: TDSServerClass;
var PersistentClass: TPersistentClass);
begin
PersistentClass := Fsrs_ServerClass.TServerMethods1;
end;

Finalement, le module Web fournit une rponse HTTP par dfaut toutes les autres actions
en retournant un simple code HTML :

procedure TWebModule2.WebModule2DefaultHandlerAction(Sender:
TObject;
Request: TWebRequest; Response: TWebResponse; var Handled:
Boolean);
begin
Response.Content := '<html><heading/><body>' +
'DataSnap Server</body></html>';
end;

Cette action est configure lors de la conception comme suit :

Actions = <
item
Default = True
Name = 'DefaultHandler'
PathInfo = '/'
OnAction = WebModule2DefaultHandlerAction
end>
CLASSE SERVEUR EXEMPLE GNRE PAR LASSISTANT
DATASNAP WEBBROKER
La troisime unit gnre par lassistant DataSnap WebBroker est la classe serveur
exemple celle qui prsente les mthodes invoques distance via REST. Il sagit de la
classe Delphi connecte au composant DSServerClass via le gestionnaire dvnements
DSServerClass1GetClass mentionn prcdemment, qui rcupre lessentiel du code rel.
La classe squelette ainsi gnre est trs simple et dtermine par le fait que des
exemples de mthodes sont demands dans lassistant. En voici le code :
type
TServerMethods1 = class(TDSServerModule)
private
{ Private declarations }
public

function EchoString(Value: string): string;


end;
On notera que cette classe hrite de la classe TDSServerModule, qui sapparente un
module de donnes standard (avec support des composants DataSetProvide), mais repose
sur une option spcifique de compilation permettant une forme de gnration RTTI pour les
mthodes publiques (antrieure la nouvelle extension RTTI de Delphi 2010) : {$MethodInfo
ON}.
La mthode EchoString par dfaut retourne simplement le paramtre transmis, mais a t
lgrement mise jour pour rpter la queue de la chane comme dans un rel cho :
function TServerMethods1.EchoString(Value: string): string;
begin
Result := Value + '...' +
Copy (Value, 2, maxint) + '...' +
Copy (Value, Length (Value) - 1, 2);
end;

COMPILATION ET TEST DU SERVEUR REST


Il est maintenant temps de compiler le serveur et de vrifier son bon fonctionnement. Une
fois effectues la compilation et lexcution du programme, il sera ncessaire dexcuter le
Dbogueur dapplication Web (disponible dans le menu Outils de Delphi) et de le dmarrer
( laide du bouton correspondant) :
Le Dbogueur dapplication Web fonctionne sur un port spcifique dans ma configuration
8081, comme le montre lillustration ci-dessus. Il est possible douvrir lURL par dfaut pour
voir les diffrentes applications disponibles dans cette architecture ou de saisir lURL
spcifique du programme, qui prend le format ApplicationName.ServerClass. Dans le cas qui
nous intresse, les deux jetons sont identiques, de sorte que lURL du serveur devient :

http://localhost:8081/FirstSimpleRestServer.FirstSimpleRestSe
rver

En louvrant dans un navigateur Web, il est possible de vrifier si le Dbogueur dapplication


Web et le serveur spcifique fonctionnent (le serveur spcifique doit tre dj en excution
car le Dbogueur dapplication Web ne le dmarrera pas automatiquement). Le navigateur
affichera alors ce qui suit :

Pour rappel, il sagit l du HTML retourn par le programme pour laction standard ce qui,
bien sr, nest pas dun grand intrt...
Ltape suivante consiste utiliser lURL spcifique pour la seule requte excutable par le
serveur REST linvocation de la mthode EchoString du TServerMethods1 du support
rest du serveur datasnap . LURL est automatiquement combine avec ajout du
prfixe du serveur REST (/datasnap/rest, par dfaut), du nom de classe, du nom de mthode
et des paramtres de mthode :

/datasnap/rest/TServerMethods1/EchoString/hello%20world

Dans lURL, %20 remplace simplement un espace (il est galement possible de saisir un
espace dans le navigateur). La rponse JSON du serveur REST apparatra alors sous la
forme suivante :

Pendant l'excution de ces tests, il est possible d'utiliser le Dbogueur dapplication Web
pour dterminer quelles requtes et rponses HTTP doivent effectivement tre transfres.
La page illustre ci-dessus est gnre par une requte de navigateur :
GET /FirstSimpleRestServer.FirstSimpleRestServer/
datasnap/rest/TServerMethods1/EchoString/hello%20world
HTTP/1.1
Host: localhost:8081
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US)
AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.27
Safari/532.0
Accept:
application/xml,application/xhtml+xml,text/html;q=0.9,
text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate,sdch
Cookie:
LastProgID=FirstSimpleRestServer.FirstSimpleRestServer
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

Cette requte retourne la rponse HTTP complte suivante :

HTTP/1.1 200 200 OK


Connection: close
Content-Type: TEXT/HTML
Content-Length: 44

{"result":["hello world...ello world...ld"]}

Comme mentionn prcdemment, la simplicit daccs ces informations peut savrer trs
utile pour dboguer les applications HTTP.

INVOCATION DU SERVEUR REST A PARTIR DUN SIMPLE


CLIENT DELPHI
Aprs avoir dvelopp le serveur et vrifi son bon fonctionnement, il est temps de
dvelopper une application client Delphi pour le tester. Deux approches diffrentes peuvent
tre adoptes, la premire consistant dvelopper un client Delphi DataSnap en utilisant la
couche spcifique de transport fournie par REST. Cette option na toutefois que peu
davantage par rapport celle consistant utiliser les couches de transport HTTP ou TCP de
DataSnap.
La seconde possibilit celle choisie dans ce chapitre est de crer un client REST
spcifique, de la mme manire que les autres clients dvelopps dans la premire partie de
ce livre blanc. Ainsi, nimporte quel autre langage pourra tre utilis pour ce dveloppement
qui ne repose pas sur un support spcifique. Il suffira pour cela de crer une application
Delphi VCL standard et dy ajouter un composant IdHTTP pour lexcution effective de la
requte REST, une bote ddition pour les entres et un bouton, avec le code suivant :
const
strServerUrl = 'http://localhost:8081/' +
'FirstSimpleRestServer.FirstSimpleRestServer/';
strMethodUrl = 'datasnap/rest/TServerMethods1/EchoString/';
procedure TFormFirstRestClient.btnPlainCallClick(Sender:
TObject);
var
strParam: string;
begin
strParam := edInput.Text;
ShowMessage (IdHTTP1.Get(strServerUrl + strMethodUrl +
strParam));
end;
Cet appel cre une URL approprie en concatnant l'adresse serveur, le chemin relatif pour
accder la mthode donne avec le serveur REST et le paramtre unique. Il retourne la
rponse suivante :

Une autre option, prsentant plus d'intrt, consiste extraire les informations relles de la
structure de donnes JSON retourne par le serveur. Une approche manuelle pourrait tre
adopte (comme il est montr prcdemment), mais dans ce cas, il peut tre prfrable de
profiter du support JSON disponible dans Delphi 2010.
Ce nouveau support JSON de Delphi est propos via une srie de classes dfinies dans
DBXJSON, qui malgr son nom peut aussi tre utilis dans les applications non lies au
framework dbExpress. Lunit DBXJSON dfinit des classes utilisables pour manipuler
diffrents types de donnes JSON (valeurs individuelles de diffrents types, rseaux, objets,
etc.). Ceci est trs utile pour personnaliser le rsultat des applications serveur (comme nous
le verrons dans le projet suivant) et pour lire les donnes reues par un client, comme dans
le cas qui nous intresse.
Les donnes JSON retournes par le serveur sont prsentes sous forme de chane, mais le
support serveur REST cre un objet associ une valeur nomme (ou paire ) et place la
valeur relle dans une gamme. Cest la raison pour laquelle, aprs analyse du rsultat HTTP
dans une structure de donnes JSON, il est ncessaire de naviguer de lobjet la paire qu'il
contient - et de la paire jusqu llment unique quelle contient :

procedure TFormFirstRestClient.btnToJSONClick(Sender:
TObject);
var
strParam, strHttpResult, strResult: string;
jValue: TJSONValue;
jObj: TJSONObject;
jPair: TJSONPair;
jArray: TJSOnArray;
begin
strParam := edInput.Text;
strHttpResult := IdHTTP1.Get(strServerUrl +
strMethodUrl + strParam);
jValue := TJSONObject.ParseJSONValue(
TEncoding.ASCII.GetBytes(strHttpResult), 0);
if not Assigned (jValue) then
begin
ShowMessage ('Error in parsing ' + strHttpResult);
Exit;
end;

try
jObj := jValue as TJSONObject;
jPair := jObj.Get(0); // Obtention de la seule et unique
paire JSON
jArray := jPair.JsonValue as TJsonArray; // La valeur de
paire est un array
strResult := jArray.Get(0).Value; // Premier et seul
lment dArray
ShowMessage ('The response is: ' + strResult);
finally
jObj.Free;
end;
end;

L encore, la complexit est due la structure de donnes retourne par le serveur, puisque
dans dautres circonstances, il serait bien plus simple danalyser les donnes JSON
rsultantes et dy accder.
APPEL DU SERVEUR REST A PARTIR DUNE APPLICATION
WEB AJAX
Pour un simple transfert dobjet dune application Delphi serveur une autre, il existe de
nombreuses alternatives lutilisation de JSON approche particulirement adapte pour
appeler le serveur Delphi compil partir dune application JavaScript excute dans le
navigateur. Ce cas dutilisation est trs pertinent dans la mesure o la technologie AJAX
(appels JavaScript asynchrones effectus dans le navigateur Web) a t et demeure lun
des lments moteur de ladoption REST. Lappel dun serveur SOAP correspondant laide
dun programme bas sur navigateur est largement plus complexe.
Comment donc crer une application reproduisant le client prcdemment dvelopp tout en
tant excute dans le navigateur Web ? De multiples mthodologies et bibliothques
pourraient tre utilises, mais loption privilgie ce stade est celle de la bibliothque open-
source JavaScript jQuery, disponible sur :
http://jquery.com
Une prsentation approfondie de jQuery et de ses modes dutilisation serait trop longue pour
ce livre blanc, mais nous expliquerons nanmoins le code jQuery sous-jacent de cet
exemple prcis. Tout dabord, la page HTML inclut jQuery et son support JSON :
<head>
<title>jQuery and Delphi 2010 REST</title>
<script
src="http://jqueryjs.googlecode.com/files/jquery-
1.3.2.min.js"
type="text/javascript"></script>
<script
src="http://jquery-json.googlecode.com/files/jquery.json-
2.2.min.js"
type="text/javascript"></script>
</head>

Par ailleurs, la page a une interface utilisateur trs simple, avec du texte, un champ d'entre
et un bouton (sans fichiers CSS sophistiqus et graphiques ajouts lobjectif tant de se
concentrer sur le point qui nous intresse ici) :
<body>
<h1>jQuery and Delphi 2010 REST</h1>

<p>This example demonstrates basic use of jQuery calling a


barebone Delphi 2010 REST server. </p>
<p>Insert the text to "Echo":

<br/>
<input type="text" id="inputText" size="50"
value="This is a message from jQuery">
<br/>
<input type="button" value="Echo" id="buttonEcho">

<div id="result">Result goes here: </div>


</body>
Les lignes ci-dessus constituent le squelette et il convient maintenant de passer au code
JavaScript proprement parler. Pour cela, il suffit dajouter un gestionnaire dvnements au
bouton, de lire le texte d'entre, d'effectuer l'appel REST et dafficher le rsultat. Pour
accder aux objets de la page, nous utiliserons le plus simple des slecteurs jQuery, bas
sur lID des objets, comme dans :

$("#inputText")

Cette opration retourne un objet jQuery encapsulant llment DOM de texte dentre. Pour
dfinir un gestionnaire dvnements, il est possible de transmettre un paramtre de
mthode anonyme la fonction click() du bouton. Deux nouvelles invocations sont
effectues : linvocation REST proprement parler ( partir des donnes getJSON globales)
et linvocation htmlI() pour ajouter le rsultat la page HTML de llment de sortie.
Voici la ligne complte du code sous-jacent de cette dmonstration un snippet de code
JavaScript trs compact mais peu lisible :

$(document).ready(function() {
$("#buttonEcho").click(function(e) {
$.getJSON("http://localhost:8081/"
"FirstSimpleRestServer.FirstSimpleRestServer/"
"datasnap/rest/TServerMethods1/EchoString/" +
$("#inputText").val(),
function(data) {
$("#result").html(data.result.join(''));
} );
});
});
En ouvrant simplement un fichier HTML avec le code donn, il est possible dappeler le
serveur personnalis mais uniquement si les paramtres d'autorisation navigateur
permettent un appel AJAX partir dun fichier local et destination dun serveur REST local.
La plupart des navigateurs nautorisent les invocations de serveurs REST que sur le mme
site que celui dont mane la page HTML.
Dans tous les cas, Internet Explorer semble bien fonctionner sur ce fichier local, aprs
activation des scripts locaux et demande de scurit limite (option disponible puisque le
fichier se trouve sur la machine locale ; voir les icnes de la barre d'tat) :

Sur les autres navigateurs, le serveur Web doit ncessairement retourner la page HTML et
les donnes REST ce qui ne prsente pas de difficult particulire, puisque le serveur
REST est, en fait, un serveur Web. Ainsi, pour une solution serveur (sous langle du
navigateur Web) il suffit dajouter une action au module de serveur Web (quon accrochera
lURL /file ) et de retourner un fichier HTML partir de celui-ci :
procedure TWebModule2.WebModule2DefaultHandlerAction(Sender:
TObject;
Request: TWebRequest; Response: TWebResponse; var Handled:
Boolean);
var
strRead: TStreamReader;
begin
strRead := TStreamReader.Create('jRestClient.html');
try
Response.Content := strRead.ReadToEnd;
finally
strRead.Free;
end;
end;

Il est maintenant possible de se rfrer une page serveur donne avec lURL /file, dobtenir
le fichier avec le code JavaScript et de lui faire appeler le serveur REST :

La diffrence avec l'image prcdente ne tient pas uniquement l'utilisation d'un navigateur
diffrent lURL dsigne est galement diffrente. Ce second cas ne repose pas sur le
chargement dun fichier, mais utilise lapplication REST serveur comme serveur Web
complet, en retournant le code HTML utilis pour invoquer ce mme serveur via AJAX.

RETOUR ET MISE A JOUR D'OBJETS


Aprs avoir vu comment dvelopper un serveur REST trs simple avec support Delphi 2010
DataSnap, il est temps dexaminer les lignes de code ncessaires pour le rendre plus
puissant. Comme nous lavons vu, le serveur retourne des donnes JSON et convertit dans
ce format le rsultat des fonctions. Il est possible de transmettre un objet en tant que rsultat
et de le convertir. Toutefois, en pratique, il sera prfrable de garder la matrise totale du
processus en crant des objets JSON spcifiques au niveau serveur et en les retournant.
Ceci est lun des objectifs du projet suivant.
Ce projet dmontrera galement comment mettre en uvre dautres mthodes HTTP que la
mthode get , pour rcuprer et modifier un objet de niveau serveur partir dun
simple client bas sur navigateur et crit en JavaScript. Enfin, nous aborderons les
problmatiques de gestion des URL et dcouvrirons notamment comment amliorer leur
flexibilit.
RETOUR DOBJETS ET DE VALEURS JSON
Dans ce second projet, nous utiliserons lassistant DataSnap WebBroker, slectionnerons
(de nouveau) larchitecture Dbogueur dapplication Web et opterons pour une classe de
base TPersistent, puisque nous navons pas besoin de module de donnes comme classe
cible pour les appels REST. Le support RTTI spcifique tant requis, il est ncessaire
dhriter (au moins) de TPersistent et de marquer la classe avec la directive
$METHODINFO, comme lillustre le code suivant :
{$METHODINFO ON}
type
TObjectsRest = class(TPersistent)
public
function PlainData (name: string): TJSONValue;
function DataMarshal (name: string): TJSONObject;
end;
{$METHODINFO OFF}
Comme le montre cet exemple, deux fonctions ont t ajoutes la classe pour pouvoir
retourner soit une valeur, soit un objet complet. Dautres mthodes seront ultrieurement
ajoutes la classe.
La structure de donnes sous-jacente de cette application est une liste dobjets de type
personnalis (notons que le dveloppement aurait pu tre plus orient-objet , mais pour
les besoins de cette illustration, une approche simplifie a t dlibrment choisie) :

type
TMyData = class (TPersistent)
public
Name: String;
Value: Integer;
public
constructor Create (const aName: string);
end;
Les objets sont conservs dans un dictionnaire et implments laide de la classe de
conteneur gnrique TObjectDictionary<TKey,TValue> dfinie dans lunit
Generics.Collections depuis Delphi 2009. Cet objet global est initialis lors du dmarrage du
programme avec lajout dune paire d'objets prdfinis. Nous remarquerons que lajout des
objets repose sur une procdure AddToDictionary spcifique pour garantir que le nom
dobjet est conforme la cl de dictionnaire et reoit une valeur alatoire, si aucune nest
fournie :
var
DataDict: TObjectDictionary <string,TMyData>;

procedure AddToDictionary (const aName: string; nVal: Integer


= -1);
var
md: TMyData;
begin
md := TMyData.Create (aName);
if nVal <> -1 then

md.Value := nVal;
DataDict.Add(aName, md);
end;

initialization
DataDict := TObjectDictionary <string,TMyData>.Create;
AddToDictionary('Sample');
Une fois cette structure de donnes implmente, il est temps daborder les deux premires
mthodes exemple utilises pour retourner les valeurs JSON. La premire retourne la valeur
de lobjet donn (en choisissant une valeur par dfaut si aucun paramtre nest transmis la
fonction) :

function TObjectsRest.PlainData(name: string): TJSONValue;


begin
if Name = '' then
name := 'Sample'; // default
Result := TJSONNumber.Create(DataDict[name].Value);
end;

Si nous utilisons une URL avec ou sans le paramtre (comme dans les deux lignes
suivantes) :

/datasnap/rest/TObjectsRest/PlainData/Test
/datasnap/rest/TObjectsRest/PlainData

nous obtiendrons tout de mme une rponse JSON, pour lobjet spcifique ou pour un objet
par dfaut :

{"result":[8978]}

Comment retourner un objet complet et non une valeur spcifique ? Le serveur REST ne
peut pas retourner une valeur TObject car le systme nest pas capable de le convertir
automatiquement ; nanmoins, il peut utiliser le nouveau support de conversion JSON pour
convertir un objet existant en format JSON :

function TObjectsRest.DataMarshal(name: string): TJSONObject;


var
jMarshal: TJSONMarshal;
begin
jMarshal := TJSONMarshal.Create(TJSONConverter.Create);
Result := jMarshal.Marshal(DataDict[name]) as TJSONObject;
end;

Cette approche est surtout utile pour recrer lobjet dans lapplication cliente Delphi et ne
prsente pas grand intrt dans les cas o le client est dvelopp dans un autre langage. Le
code JSON correspondant est assez lourd :

{"result":[{
"type":"ObjectsRestServer_Classes.TMyData",
"id":1,
"fields": {
"Name":"Test",
"Value":8068}
}]}
Quelle est donc la meilleure option pour retourner un objet JSON ? Nous recommanderons
probablement d'en crer un au niveau serveur laide de classes de support comme nous
lavons fait dans la fonction MyData :
function TObjectsRest.MyData(name: string): TJSONObject;
var
md: TMyData;
begin
md := DataDict[name];
Result := TJSONObject.Create;
Result.AddPair(
TJSONPair.Create ('Name', md.Name));
Result.AddPair(
TJSONPair.Create ('Value',
TJSONNumber.Create(md.Value)));
end;

Nous avons donc cr un code TJSONObject et ajout deux paires de proprits pour le
nom et la valeur. Nous aurions pu utiliser un nom dynamique (cest--dire la dsignation du
nom faisant partie de la paire), mais cela aurait complexifi la rcupration des donnes sur
le client. Le rsultat de ce code retournera un code JSON plus net, semblable lexemple
suivant :

{"result":[{
"Name":"Test",
"Value":8068
}]}

LISTE DOBJETS AVEC TJSONARRAY


Maintenant que nous disposons dune liste dobjets, il peut savrer ncessaire de pouvoir y
accder. Il pourra tre utile de disposer d'une liste des noms uniquement (sans donnes)
pour dvelopper une interface utilisateur au niveau client.
Pour retourner une liste, il est possible dutiliser un groupe TJSONArray en loccurrence,
un groupe de chanes cr avec un numrateur sur les cls du dictionnaire :
function TObjectsRest.List: TJSONArray;
var
str: string;
begin
Result := TJSONArray.Create;
for str in DataDict.Keys do
begin
Result.Add(str);
end;
end;
Cet appel retourne un groupe sous format JSON, qui est son tour transmis (selon le
processus habituel) dans un groupe dsign rsultat (do la double notation de groupe
imbriqu) :
{"result":[
["Test","Sample"]
]}
Aprs avoir vu comment retourner une liste de valeurs et extraire les donnes de chaque
lment individuel, nous pouvons maintenant commencer dvelopper linterface utilisateur.
AU NIVEAU CLIENT : LISTE ET VALEURS
Au lieu de dvelopper le code HTML initial avec la liste de valeurs et de laisser lutilisateur en
choisir une, il est possible dexploiter les fonctionnalits compltes du modle AJAX.
Au dmarrage, la page ne contiendra pas de donnes, mais uniquement les lments HTML
et le code JavaScript. Ds que la page est charge et mme sans intervention de
lutilisateur elle contacte le serveur pour requrir les donnes relles et les transfrer dans
linterface utilisateur.
Ainsi, au dmarrage, le programme affiche la valeur de lobjet Exemple, en utilisant les
lments HTML et linvocation AJAX ci-dessous (lexcution se fait comme lorsque le
document est prt, c'est--dire lorsque le DOM a termin le chargement) :

<div>Sample: <span id="sample"></span></div>

<script>
var baseUrl = "/ObjectsRestServer.RestObjects/dataSnap" +
"/rest/TObjectsRest/";
$(document).ready(function() {
$.getJSON(baseUrl + "MyData/Sample",
function(data) {
strResult = data.result[0].Value;
$("#sample").html(strResult);
} );

Linvocation AJAX de MyData transmet le nom dobjet comme nouveau paramtre URL et
extrait du groupe de rsultat la valeur nomme proprit/paire en laffichant dans un lment
HTML avec espace vide. Un processus similaire (bien qu'un peu plus complexe) a lieu pour
la liste. Il sagit l encore dun appel AJAX, mais cette fois, le code HTML rsultant doit tre
dvelopp. L'opration est excute dans une fonction refreshList spare, appele
automatiquement au dmarrage et manuellement par lutilisateur :

<div>Current entries list:


<a href="#" id="refresh">Refresh</a>
<span id="list"></span></div>

function refreshList()
{
$.getJSON(baseUrl + "list",
function(data) {
thearray = data.result[0];
var ratingMarkup = ["<br>"];
for (var i=0; i < thearray.length; i++) {
ratingMarkup = ratingMarkup +
"<a href='#'>" + thearray[i] +
"</a><br>";
}
$("#list").html(ratingMarkup);
} );
};

Le code utilise une boucle pour pour vrifier le groupe gnr. Nous aurions pu utiliser le
mcanisme dnumration $.each de jQuery, mais cela aurait complexifi la lecture du code.
La boucle cre le code HTML, qui saffiche ensuite dans lemplacement espace avec lID
donne. Il sagit dune sortie individuelle incluant la valeur de lobjet Sample (le code
dcrit prcdemment) et la liste des valeurs retournes dans le groupe JSON :

Comme mentionn prcdemment, la fonction refreshList est invoque au dmarrage (dans


le gestionnaire dvnements disponibles) ; elle est galement connecte un lien
correspondant pour permettre lutilisateur de rafrachir ultrieurement les donnes de la
liste sans avoir rafrachir lensemble de la page HTML :

$(document).ready(function() {
refreshList();

$("#refresh").click(function(e) {
refreshList();
} );

Quelques oprations restent encore pour gnrer le code Ds que le code HTML est prt
pour la liste (il sagit dune liste de liens), il doit tre accroch ces liens pour que
chaque entre de la liste dans lapplication cliente, lorsquelle est slectionne, entrane le
chargement de lobjet correspondant au niveau serveur. Linterface utilisateur des donnes
objet est constitue de deux botes dentre, qui seront ultrieurement utilises pour
manipuler les donnes objet. Le comportement est ajout chaque point dancrage dans le
conteneur de liste.

$("#list").find("a").click(function(e) {
var wasclicked = $(this);
$.getJSON(baseUrl + "MyData/" + $(this).html(),
function(data) {
strResult = data.result[0].Value;
$("#inputName").val(wasclicked.html());
$("#inputValue").val(strResult);
} );
});

Notons lutilisation de lappel $(this), qui constitue plus ou moins le paramtre metteur pour
un vnement Delphi. Le contenu html de llment slectionn est son texte, qui est le nom
de llment transmettre au serveur dans lURL avec lexpression :

baseUrl + "MyData/" + $(this).html()

Aprs avoir dvelopp ce code, on pourra observer laction produite lorsque lon clique sur
lun des lments de la liste : une nouvelle invocation AJAX est effectue destination du
serveur pour demander une valeur donne et la valeur retourne saffiche dans deux botes
dentre texte :

Comme le montre cette illustration, le programme permet de rcuprer une valeur, mais
comporte galement trois boutons pour excuter les oprations les plus communes
(Cration/Lecture/Mise jour/Suppression). Ces actions sont supportes en HTML laide
des 4 mthodes HTML de code (respectivement, PUT, GET, POST et DELETE). Dans le
chapitre suivant, nous verrons comment ces mthodes sont supportes par un serveur
REST Delphi 2010.
POST, PUT ET DELETE
Jusqu prsent, nous avons vu comment obtenir des donnes du serveur REST. Mais quen
est-il de leur mise jour ? Lide gnralement admise pour lenvironnement REST est quil
faut viter dutiliser des URL spcifiques pour identifier les oprations et quil est prfrable
de nen utiliser quune seule pour identifier les objets serveur (comme MyData/Sample dans
le cas dvelopp ici) et dutiliser les mthodes HTTP pour indiquer les actions excuter.
Il serait regrettable que le support REST de Delphi se contente d'associer les URL aux
mthodes ; mais au contraire, il relie non seulement les URL, mais galement la mthode
HTTP aux mthodes selon un schma relativement simple : le nom de lopration est
initialement associ au nom de mthode laide du mappage suivant :
GET obtention (par dfaut, peut tre omis)
POST mise jour
PUT acceptation
DELETE annulation
Ces mappages peuvent tre personnaliss en manipulant les quatre gestionnaires
dvnements correspondants du composant DSHTTPWebDispatcher. En cas de choix des
rgles de nommage standards, le support des diffrentes oprations exigera de dfinir la
classe serveur comme suit :

type
TObjectsRest = class(TPersistent)
public
function List: TJSONArray;
function MyData (name: string): TJSONObject;
procedure updateMyData (name, value: string);
procedure cancelMyData (name: string);
procedure acceptMyData (name, value: string);
end;

Pour obtenir ou supprimer un lment, seul le nom est ncessaire ; en revanche, la cration
ou la mise jour dun lment exigera un second paramtre, en plus des donnes.
Limplmentation des trois nouvelles mthodes est assez simple et directe, notamment parce
quelles nont pas retourner une valeur (il va sans dire quon aura vrifi au pralable que
les paramtres ne sont pas vides et que l'objet serveur existe rellement) :
procedure TObjectsRest.updateMyData (name, value: string);
begin
DataDict[name].Value := StrToIntDef (Value, 0);
end;

procedure TObjectsRest.cancelMyData(name: string);


begin
DataDict.Remove(name);
end;

procedure TObjectsRest.acceptMyData(name, value: string);


begin
AddToDictionary (name, StrToIntDef (Value, 0));
end;

EDITION AU NIVEAU CLIENT


Maintenant que les oprations CRUD sont disponibles sur le serveur REST, il est possible
de complter lapplication client JavaScript laide du code des trois boutons ddition
(lillustration incluant linterface utilisateur base sur navigateur figure plus haut dans ce livre
blanc).
jQuery dispose dun support spcifique de lopration get (avec diffrentes versions,
notamment la version spcifique JSON utilise prcdemment) et dun autre support pour
les oprations post ; en revanche, pour les autres mthodes HTTL, il est ncessaire
dutiliser l'invocation de niveau infrieur (et lgrement plus complexe) $.ajax. Cette
invocation comporte, parmi ses paramtres, une liste de valeurs associes par paires, sur
une douzaine de possibilits. Les paramtres les plus pertinents sont le type et lurl, tandis
que les donnes permettent de transmettre dautres paramtres POST.
La mise jour est relativement simple et il est possible de fournir lintgralit des donnes au
serveur REST laide de lURL :

$("#buttonUpdate").click(function(e) {
$.ajax({
type: "POST",
url: baseUrl + "MyData/" +
$("#inputName").val() + "/" +
$("#inputValue").val(),
success: function(msg){
$("#log").html(msg);
}
});
});

La suppression est tout aussi simple, exigeant uniquement de crer lURL avec la rfrence
lobjet supprimer :
$("#buttonDelete").click(function(e) {
$.ajax({
type: "DELETE",
url: baseUrl + "MyData/" +
$("#inputName").val(),
success: function(msg){
$("#log").html(msg);
}
});
});

L'implmentation PUT sannonait plus problmatique, puisquen labsence de donnes


fournies, certains navigateurs (notamment Chrome) affichent les donnes comme non-
dfinies -- ce qui entrane une erreur de lanalyse dentre HTTP du serveur REST et un
plantage de ce dernier. Les informations devant tre transmises (et tant donn quil nest
pas possible de transmettre plus de paramtres que ceux demands par le serveur au risque
dentraner une erreur), une option consiste remplacer lun des lments dURL par un
lment de donnes correspondant :
$("#buttonNew").click(function(e) {
$.ajax({
type: 'PUT',
data: $("#inputValue").val(),
url: baseUrl + "MyData/" +
$("#inputName").val(),
success: function(msg){
$("#log").html(msg);
}
});
});

On notera que la documentation jQuery dconseille expressment dutiliser la mthode PUT


dans les navigateurs pour le pas risquer dobtenir des rsultats mitigs. Ceci explique peut-
tre galement pourquoi un certain nombre de services REST (notamment ceux de
Microsoft) tendent utiliser la mthode POST pour la mise jour et la cration dobjets
serveur. Il est toutefois prfrable, chaque fois que possible, daborder les deux notions
sparment pour plus de clart et de cohrence.
Ainsi, avec les trois mthodes supplmentaires ajoutes la classe et les invocations
JavaScript adquates, nous disposons maintenant dun exemple avec une interface
utilisateur complte, base sur navigateur, pour crer et diter des objets dans le serveur
REST. Lillustration suivante prsente quelques objets crs :

SERVEURS REST ORIENTS-DONNES


Si lobjectif initial sous-jacent de DataSnap est de dplacer des tables de donnes dun
serveur de niveau intermdiaire une application cliente, il peut paratre tonnant de
constater quil est impossible de retourner un jeu de donnes partir dun serveur REST
dvelopp dans Delphi 2010. Plus prcisment, cette opration ne peut pas tre excute
directement ni aussi simplement que pour sa reprsentation XML, mais il est possible de
crer un rsultat JSON avec toutes les donnes du jeu de donnes Delphi. Cest lobjet du
dernier exemple dvelopp dans ce livre blanc.
Le programme est assez lmentaire, puisquil se contente de retourner les donnes dun
Jeu complet, sans mtadonnes. Il aurait bien besoin de diffrentes extensions et
daffinages de son interface utilisateur, mais fournit toutefois une base suffisante pour
commencer. La classe serveur ne comporte quune seule mthode, qui retourne un jeu
complet de donnes dans un groupe JSON contenant des objets/enregistrements
individuels :

function TServerData.Data: TJSONArray;


var
jRecord: TJSONObject;
I: Integer;
begin
ClientDataSet1.Open;
Result := TJSonArray.Create;

while not ClientDataSet1.EOF do


begin
jRecord := TJSONObject.Create;
for I := 0 to ClientDataSet1.FieldCount - 1 do
jRecord.AddPair(
ClientDataSet1.Fields[I].FieldName,
TJSONString.Create
(ClientDataSet1.Fields[I].AsString));
Result.AddElement(jRecord);
ClientDataSet1.Next;
end;
end;

Cette mthode est invoque par lapplication cliente aprs le chargement de la page,
travers la construction dynamique d'une table HTML avec le code jQuery suivant (semblable
au code amplement dvelopp dans les chapitres prcdents) :
$(document).ready(function() {

$.getJSON(
"/DataRestServer.RestDataServer/datasnap/rest/TServerDa
ta/Data",
function(data) {
thearray = data.result[0];
var ratingMarkup = "<table border='1'>";
for (var i=0; i < thearray.length; i++) {

ratingMarkup = ratingMarkup + "<tr><td>" +


thearray[i].Company +
"</td><td>" +
thearray[i].City +
"</td><td>" +
thearray[i].State +
"</td><td>" +
thearray[i].Country +
"</td></tr>";
}
ratingMarkup = ratingMarkup + "</table>";
$("#result").html(ratingMarkup);
} );
});
Le rsultat lmentaire saffichant dans Internet Explorer est le suivant :
Peut-il tre amlior ? Est-il possible, par exemple, de ne pas lister l'intgralit des champs
afficher dans JavaScript, lorsque tous sont requis ? La version finale du programme ajoute
un support des mtadonnes pour amliorer la sortie finale.
Du ct serveur, une seconde fonction retourne un groupe de noms de champs partir des
dfinitions de champs de jeu de donnes :

function TServerData.Meta: TJSONArray;


var
jRecord: TJSONObject;
I: Integer;
begin
ClientDataSet1.Open;
Result := TJSonArray.Create;

for I := 0 to ClientDataSet1.FieldDefs.Count - 1 do
Result.Add(ClientDataSet1.FieldDefs[I].Name);
end;
Le JavaScript au niveau client a t tendu par une seconde invocation du serveur REST
pour obtenir les mtadonnes :
$.getJSON(
"/DataRestServer.RestDataServer/datasnap/rest/TServerDa
ta/Meta",
function(data) {
theMetaArray = data.result[0];

Ces informations sont utilises pour crer lentte de table et accder dynamiquement aux
proprits dobjet, avec l'objet de notation [propertyname]. Dans le cas qui nous intresse,
le code existant utilis pour accder un objet avec le symbole de proprit :

thearray[i].Company

devient le code ci-dessous, qui lit la proprit par nom, en utilisant le nom du champ
enregistr dans les mtadonnes :
thearray[i][theMetaArray[j]].

Voici le code JavaScript complet utilis pour crer le balisage HTML :


var ratingMarkup = "<table border='1'><tr>";
// header
for (var j=0; j < theMetaArray.length; j++) {

ratingMarkup = ratingMarkup + "<th>" +


theMetaArray[j] + "</th>";
};
ratingMarkup = ratingMarkup + "</tr>";

// content
for (var i=0; i < thearray.length; i++) {
ratingMarkup = ratingMarkup + "<tr>";
for (var j=0; j < theMetaArray.length; j++) {
ratingMarkup = ratingMarkup + "<td>" +
thearray[i][theMetaArray[j]] + "</td>";
};
ratingMarkup = ratingMarkup + "</tr>";
}
ratingMarkup = ratingMarkup + "</table>";

Le rsultat de cette version tendue gagne lgrement en simplicit (et en flexibilit) :

L encore, cet exemple noffre quune base de dpart et n'utilise aucune des extensions
jQuery, qui ajouteraient des fonctionnalits significatives aux tables HTML en les
transformant en puissantes grilles dinterface utilisateur avec des outils de tri, de filtrage et
ddition.

CONCLUSION
Ce livre blanc ne fait queffleurer larchitecture REST et le dveloppement dapplications
REST client et serveur avec Delphi 2010. Il fournit un aperu trs rapide des technologies
associes (XML, JSON, XPath, JavaScript, jQuery) qui peuvent tre ncessaires pour
devenir expert des architectures REST.
tant donn le nombre croissant de serveurs REST publics, lmergence du cloud-
computing et lintrt grandissant pour lhbergement dapplications Web, Delphi pourrait
jouer un rle significatif dans le dveloppement de riches clients dinterface appelant des
serveurs distants et dans le dveloppement de serveurs rels de manipulation de structures
de donnes dans une application cliente (dveloppe dans nimporte quel langage) ou
directement dans le navigateur.
Comme lont montr les dmonstrations finales, la combinaison de JavaScript et dun
serveur REST Delphi permet dutiliser lEDI Embarcadero pour dvelopper des applications
Web professionnelles, modernes et de qualit optimale.

A PROPOS DE LAUTEUR
Marco est lauteur du best-seller Mastering Delphi et a publi, au cours des dernires
annes, des ouvrages sur les dernires versions de Delphi, notamment Delphi 2007
Handbook , Delphi 2009 Handbook et le Delphi 2010 Handbook (actuellement en
cours de rdaction).
Paralllement ses prestations de formation et de consulting sur Delphi, Marco propose des
services de consulting sur les architectures Web et lintgration Web des projets Delphi
sous langle de linvocation dautres serveurs et de leur mise disposition.
Vous pouvez consulter le blog de Marco sur http://blog.marcocantu.com, changer avec lui
sur Twitter sur http://twitter.com/marcocantu ou le contacter par email marco.cantu
@gmail.com.
Marco souhaite remercier Daniele Teti pour sa contribution llaboration de ce livre blanc,
tout particulirement sur le support JSON de Delphi 2010.
Embarcadero Technologies, Inc. est un diteur leader de solutions primes pour les
dveloppeurs dapplications et les professionnels des bases de donnes pour les aider
concevoir et excuter plus efficacement et rapidement leurs solutions quels que soient les
langages de programmation ou les plates-formes. 90 des 100 premires socits du
classement Fortune et une communaut active de plus de 3 millions dutilisateurs dans le
monde font confiance aux outils Embarcadero pour maximiser leur productivit, rduire les
cots, simplifier la gestion du changement et de la conformit et acclrer linnovation. Ses
produits les plus emblmatiques sont notamment : Embarcadero Change Manager,
Embarcadero RAD Studio, DBArtisan, Delphi, ER/Studio, JBuilder et Rapid SQL.
Cr en 1993, le sige dEmbarcadero se situe San Francisco ; lentreprise est prsente
dans le monde entier et son site Web peut tre consult ladresse www.embarcadero.com

Vous aimerez peut-être aussi