connections with datasnap

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 Unit1, Unit2, and ServerMethods1. (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 QCDispatcher, and the unit that contains my QualityCentral-specific logic is called QCMethods. 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 TDSServerModule, which implements the IAppServer interface. This is our starting point for the server methods: TQC = class(TDSServerModule) private { Private declarations } public { Public declarations } function EchoString(Value: string): string; function ReverseString(Value: string): string; end; Tracking HTTP request and response To track the connection-specific information for the user, we need threadvars 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

Upload: zerocalls

Post on 27-Dec-2015

29 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Connections With DataSnap

By: John Kaster

Abstract: See how to access the current connection's web request and web response in your DataSnap/RESTserver methods unit

In This ArticleIntroductionTracking HTTP request and responseUsing connection info with DataSnap server methodsA Quick TestDataSnap/REST calling patternSupporting load-balanced connectionsThe door is open

IntroductionDataSnap is a distributed computing technology available in RAD Studio. The RAD Studio XE release greatlyincreases 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, sodevelopers can usually forget about the communication and marshalling mechanics, and focus directly onimplementing custom logic in the DataSnap server's methods. Sometimes, however, the active user connection isneeded in the server methods. Read on to find out how this can be easily accomplished with a DataSnap/RESTserver.

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 RADStudio XE using DataSnap/REST. To support the existing single sign-on system for EDN, we need to be able toretrieve the HTTP request for the user communicating with the QC server.

The units generated by the DataSnap/REST wizard are usually named Unit1, Unit2, and ServerMethods1.(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 ofdata and calls between the DataSnap server and the REST clients is called QCDispatcher, and the unit thatcontains my QualityCentral-specific logic is called QCMethods. The implementation of the actual QualityCentrallogic is in a class called TQC.

Because I might want to support dataset provider/resolver operations on this DataSnap server, I selected theoption in the DataSnap wizard that allows me to descend from TDSServerModule, which implements theIAppServer interface.

This is our starting point for the server methods:

TQC = class(TDSServerModule) private { Private declarations } public { Public declarations } function EchoString(Value: string): string; function ReverseString(Value: string): string; end;

Tracking HTTP request and responseTo track the connection-specific information for the user, we need threadvars for the HTTP request and responseobjects. 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

Page 2: Connections With DataSnap

Basics web site. Because they need to be visible outside the unit in which they are defined, their declaration is inthe interface section of QCDispatcher.

Here's an excerpt from the source file:

threadvar /// <summary>Web request thread variable for the current connection</summary> Request: TWebRequest; /// <summary>Web response thread variable for the current connection</summary> Response: TWebResponse;

var WebModuleClass: TComponentClass = TQCDispatch;

implementation

uses QCMethods, WebReq;

We can use the BeforeDispatch and AfterDispatch methods of QCDispatcher webmodule to manage thevalues assigned to these threadvars. The BeforeDispatch event is triggered right after the client connection isestablished. The AfterDispatch 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 BeforeDispatch event was already generated by the wizard. The assignment of the threadvars can be addedto it.

procedure TQCDispatch.WebModuleBeforeDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);begin // Assign connection-specific Request and Response variables to this thread QCDispatcher.Request := Request; QCDispatcher.Response := Response;

if FServerFunctionInvokerAction <> nil then FServerFunctionInvokerAction.Enabled := AllowServerFunctionInvoker;end;

The AfterDispatch event can be created by clicking on the design surface for web module, and double clickingon the AfterDispatch entry in the object inspector.

This is the implementation for AfterDispatch:

procedure TQCDispatch.WebModuleAfterDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);begin QCDispatcher.Request := nil; QCDispatcher.Response := nil;end;

Note: These are the WebModule dispatch events being modified, not the WebFileDispatcher events.

Using connection info with DataSnap server methodsNow that we have thread-specific values visible to us, we can create a simple server method to verify theassignment logic of the BeforeDispatch event.

Adding the public declaration:

function UserIP: string;

to the TQC class and pressing Shift+Ctrl+C will invoke class completion and generate the stub of theimplementation for the function.

Connections with DataSnap http://edn.embarcadero.com/print/40890

2 de 7 04/07/2012 22:40

Page 3: Connections With DataSnap

function TQC.UserIP: string;begin

end;

However, the request object we need to access in this method isn't visible yet. I can add the QCDispatcher unitto my QCMethods unit by pressing Alt+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), andclicking 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. IfI invoke it again after using QCDispatcher, the only other unit in my project is displayed. (The radio button alsodefaults to the last option selected.)

The form unit is not needed in QCMethods, so I cancelled out of the dialog. (I just wanted to describe theintelligence of "Use Unit".)

The implementation of UserIP is pretty simple so far:

function TQC.UserIP: string;begin Result := QCDispatcher.Request.RemoteAddr;end;

I'm using a VCL form application for DataSnap because it's by far the easiest way to iteratively develop, test, anddebug a DataSnap server. The DataSnap ISAPI article I mentioned earlier shows the steps for converting fromone type of DataSnap server to another.

A Quick TestI'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

Page 4: Connections With DataSnap

Click one of the Run buttons. (The first time running the app usually displays a Windows firewall prompt to allowor deny it access to the port.)

Click Open Browser 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

Page 5: Connections With DataSnap

Click the Server Functions link, which will open another browser window.

Expand TQC's methods and click the Execute button on UserIP, to see the local IP address returned. If you wantto verify the results on another machine just to make sure you see a different IP address, you can use somethinglike 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

Page 6: Connections With DataSnap

You can right mouse click on the desired request, and copy the location to your clipboard. In this case, it'shttp://localhost:8080/datasnap/rest/TQC/UserIP/. To test from a different machine, I just need to replacelocalhost with the name of the machine running the DataSnap server. In this case, that's jfkm6300.

Here's the result of the request from another machine on my home network.

DataSnap/REST calling patternAs you can probably figure out from the URL pattern above, this is the default pattern for a DataSnap/REST call:

http://servername[:port]/datasnap/rest/classname/method/arg1/arg2/...argN

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 anotherarticle. In the meantime, if you'd like to explore it on your own, you can look at functionServerFunctionExecutor(className, connectionInfo, owner) in the ServerFunctionExecutor.js that getsgenerated for your DataSnap/REST project, or at the various proxies you can generate for a DataSnap/RESTclient.

Connections with DataSnap http://edn.embarcadero.com/print/40890

6 de 7 04/07/2012 22:40

Page 7: Connections With DataSnap

Supporting load-balanced connectionsThe next step for the UserIP method is correctly handling requests coming through a load balancer, where thestandard user address for the HTTP request is the IP of the load balancer, and a custom header has to beexamined to get the end-user IP address. These are the relevant routines to conditionally extract the value ofthis header from the HTTP request if it exists:

function StrIsEmpty(const AInput: string) : boolean;begin Result := Length(Trim(AInput)) = 0;end;

function StrIsFull(const AInput: string): boolean;begin Result := not StrIsEmpty(AInput);end;

function UserHostAddress(const ARequest: TWebRequest): string;const cnXForwardedFor = 'x-forwarded-for';var lStr: string; lParts: TStringDynArray; lIndex: Integer;begin lStr := String(ARequest.GetFieldByName(cnXForwardedFor)); if StrIsFull(lStr) then begin lParts := SplitString(lStr, ','); lIndex := High(lParts); while ((lIndex >= Low(lParts)) and (StrIsEmpty(lParts[lIndex]))) do Dec(lIndex); Result := String(lParts[lIndex]); end else Result := String(ARequest.RemoteAddr);end;

As you can see from the code above, the HTTP header we're looking "x-forwarded-for". Luckily, theX-Forwarded-For header is a de facto standard, so the above routine should work with most of the availableload-balancing solutions.

Through the use of this routine, the UserIP function can be updated to understand HTTP requests in aload-balanced environment:

function TQC.UserIP: string;begin Result := UserHostAddress(QCDispatcher.Request);end;

The door is openNow that we can get specific connection information in our server methods, implementing single sign-on byretrieving HTTP packet information for the specific user, IP white listing, and other customizations are all readilyachievable.

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