Anda di halaman 1dari 7

By: John Kaster

Abstract: See how to access the current connection's web request and web response in your DataSnap/REST
server methods unit
In This Article
Introduction
Tracking HTTP request and response
Using connection info with DataSnap server methods
A Quick Test
DataSnap/REST calling pattern
Supporting load-balanced connections
The door is open
Introduction
DataSnap is a distributed computing technology available in RAD Studio. The RAD Studio XE release greatly
increases the cross-platform and cross-language reach of DataSnap, particularly for REST implementations.
The DataSnap dispatcher handles all the client-based communication and marshalling automatically, so
developers can usually forget about the communication and marshalling mechanics, and focus directly on
implementing custom logic in the DataSnap server's methods. Sometimes, however, the active user connection is
needed in the server methods. Read on to find out how this can be easily accomplished with a DataSnap/REST
server.
If you're not familiar with the process of building a DataSnap/REST server, read my previous article on building,
debugging, and deploying a DataSnap/REST ISAPI dll which covers every step.
The examples in this article are extracted from the new QualityCentral (QC) middle-tier being written with RAD
Studio XE using DataSnap/REST. To support the existing single sign-on system for EDN, we need to be able to
retrieve the HTTP request for the user communicating with the QC server.
The units generated by the DataSnap/REST wizard are usually named Uni t 1, Uni t 2, and Ser ver Met hods1.
(These names can vary if you have a project group open and adding a new project to the group via the wizard.)
This is the run-down on the new unit names: The unit that handles client communications and marshalling of
data and calls between the DataSnap server and the REST clients is called QCDi spat cher , and the unit that
contains my QualityCentral-specific logic is called QCMet hods. The implementation of the actual QualityCentral
logic is in a class called TQC.
Because I might want to support dataset provider/resolver operations on this DataSnap server, I selected the
option in the DataSnap wizard that allows me to descend from TDSSer ver Modul e, which implements the
I AppSer ver interface.
This is our starting point for the server methods:
TQC = cl ass( TDSSer ver Modul e)
pr i vat e
{ Pr i vat e decl ar at i ons }
publ i c
{ Publ i c decl ar at i ons }
f unct i on EchoSt r i ng( Val ue: st r i ng) : st r i ng;
f unct i on Rever seSt r i ng( Val ue: st r i ng) : st r i ng;
end;
Tracking HTTP request and response
To track the connection-specific information for the user, we need t hr eadvar s for the HTTP request and response
objects. For more information on threadvars, see the Embarcadero online documentation site and the Delphi
Connections with DataSnap http://edn.embarcadero.com/print/40890
1 de 7 04/07/2012 22:40
Basics web site. Because they need to be visible outside the unit in which they are defined, their declaration is in
the interface section of QCDi spat cher .
Here's an excerpt from the source file:
t hr eadvar
/ / / <summar y>Web r equest t hr ead var i abl e f or t he cur r ent connect i on</ summar y>
Request : TWebRequest ;
/ / / <summar y>Web r esponse t hr ead var i abl e f or t he cur r ent connect i on</ summar y>
Response: TWebResponse;
var
WebModul eCl ass: TComponent Cl ass = TQCDi spat ch;
i mpl ement at i on
uses QCMet hods, WebReq;
We can use the Bef or eDi spat ch and Af t er Di spat ch methods of QCDi spat cher webmodule to manage the
values assigned to these t hr eadvar s. The Bef or eDi spat ch event is triggered right after the client connection is
established. The Af t er Di spat ch method is called right before the response is sent to back to the requestor.
You can read more about the web dispatcher component on the docwiki site.
The Bef or eDi spat ch event was already generated by the wizard. The assignment of the threadvars can be added
to it.
pr ocedur e TQCDi spat ch. WebModul eBef or eDi spat ch( Sender : TObj ect ;
Request : TWebRequest ; Response: TWebResponse; var Handl ed: Bool ean) ;
begi n
/ / Assi gn connect i on- speci f i c Request and Response var i abl es t o t hi s t hr ead
QCDi spat cher . Request : = Request ;
QCDi spat cher . Response : = Response;
i f FSer ver Funct i onI nvoker Act i on <> ni l t hen
FSer ver Funct i onI nvoker Act i on. Enabl ed : = Al l owSer ver Funct i onI nvoker ;
end;
The Af t er Di spat ch event can be created by clicking on the design surface for web module, and double clicking
on the Af t er Di spat ch entry in the object inspector.
This is the implementation for Af t er Di spat ch:
pr ocedur e TQCDi spat ch. WebModul eAf t er Di spat ch( Sender : TObj ect ;
Request : TWebRequest ; Response: TWebResponse; var Handl ed: Bool ean) ;
begi n
QCDi spat cher . Request : = ni l ;
QCDi spat cher . Response : = ni l ;
end;
Note: These are the WebModule dispatch events being modified, not the WebFileDispatcher events.
Using connection info with DataSnap server methods
Now that we have thread-specific values visible to us, we can create a simple server method to verify the
assignment logic of the Bef or eDi spat ch event.
Adding the public declaration:
f unct i on User I P: st r i ng;
to the TQC class and pressing Shi f t +Ct r l +C will invoke class completion and generate the stub of the
implementation for the function.
Connections with DataSnap http://edn.embarcadero.com/print/40890
2 de 7 04/07/2012 22:40
f unct i on TQC. User I P: st r i ng;
begi n
end;
However, the request object we need to access in this method isn't visible yet. I can add the QCDi spat cher unit
to my QCMet hods unit by pressing Al t +F11 to display the "Use Unit" dialog in the editor. By selecting the unit,
indicating it should used in the implementation section (to avoid circular reference errors when compiling), and
clicking OK, the unit reference is added to the appropriate uses clause.
The "Use Unit" dialog is smart enough to only offer the units that are not already used in the active source file. If
I invoke it again after using QCDi spat cher , the only other unit in my project is displayed. (The radio button also
defaults to the last option selected.)
The form unit is not needed in QCMet hods, so I cancelled out of the dialog. (I just wanted to describe the
intelligence of "Use Unit".)
The implementation of User I P is pretty simple so far:
f unct i on TQC. User I P: st r i ng;
begi n
Resul t : = QCDi spat cher . Request . Remot eAddr ;
end;
I'm using a VCL form application for DataSnap because it's by far the easiest way to iteratively develop, test, and
debug a DataSnap server. The DataSnap ISAPI article I mentioned earlier shows the steps for converting from
one type of DataSnap server to another.
A Quick Test
I'll use the Server Function Invoker to perform a quick test.
Connections with DataSnap http://edn.embarcadero.com/print/40890
3 de 7 04/07/2012 22:40
Click one of the Run buttons. (The first time running the app usually displays a Windows firewall prompt to allow
or deny it access to the port.)
Click Open Br owser to display the DataSnap server home page in your browser.
Connections with DataSnap http://edn.embarcadero.com/print/40890
4 de 7 04/07/2012 22:40
Cl i ck the Server Functions link, which will open another browser window.
Expand TQC's methods and click the Execut e button on User I P, to see the local IP address returned. If you want
to verify the results on another machine just to make sure you see a different IP address, you can use something
like Firebug to quickly retrieve the REST URL for the request made by the Server Function Invoker.
Connections with DataSnap http://edn.embarcadero.com/print/40890
5 de 7 04/07/2012 22:40
You can right mouse click on the desired request, and copy the location to your clipboard. In this case, it's
ht t p: / / l ocal host : 8080/ dat asnap/ r est / TQC/ User I P/ . To test from a different machine, I just need to replace
l ocal host with the name of the machine running the DataSnap server. In this case, that's j f km6300.
Here's the result of the request from another machine on my home network.
DataSnap/REST calling pattern
As you can probably figure out from the URL pattern above, this is the default pattern for a DataSnap/REST call:
ht t p: / / ser ver name[ : por t ] / dat asnap/ r est / cl assname/ met hod/ ar g1/ ar g2/ . . . ar gN
servername is the name of the server or domain just a standard part of the HTTP request.
port is required if a non-standard port is assigned for either http or https.
/datasnap/rest is the pattern for special dispatch handling by the DataSnap server. (Both the "datasnap"
and "rest" values can be overridden by identifiers of your choice.)
/classname is the name of the class for the RPC call
/method is the name of the class method (function or procedure)
/arg1 /argn are the parameters passed to the method. If the arguments contain special characters,
they must be URL-encoded
Further discussion of the ways you can use HTTP to invoke DataSnap methods will have to wait for another
article. In the meantime, if you'd like to explore it on your own, you can look at function
Ser ver Funct i onExecut or ( cl assName, connect i onI nf o, owner ) in the Ser ver Funct i onExecut or . j s that gets
generated for your DataSnap/REST project, or at the various proxies you can generate for a DataSnap/REST
client.
Connections with DataSnap http://edn.embarcadero.com/print/40890
6 de 7 04/07/2012 22:40
Supporting load-balanced connections
The next step for the User I P method is correctly handling requests coming through a load balancer, where the
standard user address for the HTTP request is the IP of the load balancer, and a custom header has to be
examined to get the end-user IP address. These are the relevant routines to conditionally extract the value of
this header from the HTTP request if it exists:
f unct i on St r I sEmpt y( const AI nput : st r i ng) : bool ean;
begi n
Resul t : = Lengt h( Tr i m( AI nput ) ) = 0;
end;
f unct i on St r I sFul l ( const AI nput : st r i ng) : bool ean;
begi n
Resul t : = not St r I sEmpt y( AI nput ) ;
end;
f unct i on User Host Addr ess( const ARequest : TWebRequest ) : st r i ng;
const
cnXFor war dedFor = ' x- f or war ded- f or ' ;
var
l St r : st r i ng;
l Par t s: TSt r i ngDynAr r ay;
l I ndex: I nt eger ;
begi n
l St r : = St r i ng( ARequest . Get Fi el dByName( cnXFor war dedFor ) ) ;
i f St r I sFul l ( l St r ) t hen
begi n
l Par t s : = Spl i t St r i ng( l St r , ' , ' ) ;
l I ndex : = Hi gh( l Par t s) ;
whi l e ( ( l I ndex >= Low( l Par t s) ) and ( St r I sEmpt y( l Par t s[ l I ndex] ) ) ) do
Dec( l I ndex) ;
Resul t : = St r i ng( l Par t s[ l I ndex] ) ;
end
el se
Resul t : = St r i ng( ARequest . Remot eAddr ) ;
end;
As you can see from the code above, the HTTP header we're looking "x-forwarded-for". Luckily, the
X-Forwarded-For header is a de facto standard, so the above routine should work with most of the available
load-balancing solutions.
Through the use of this routine, the User I P function can be updated to understand HTTP requests in a
load-balanced environment:
f unct i on TQC. User I P: st r i ng;
begi n
Resul t : = User Host Addr ess( QCDi spat cher . Request ) ;
end;
The door is open
Now that we can get specific connection information in our server methods, implementing single sign-on by
retrieving HTTP packet information for the specific user, IP white listing, and other customizations are all readily
achievable.
There is certainly plenty more to write about DataSnap.
Stay tuned!
Published on: 10/5/2010 12:52:21 PM
Server Response from: ETNASC04
Copyright 1994 - 2012 Embarcadero Technologies, Inc. All rights reserved.
Connections with DataSnap http://edn.embarcadero.com/print/40890
7 de 7 04/07/2012 22:40

Anda mungkin juga menyukai