25/11/2013
RESTful interfaces and dynamic
web pages with Delphi
Daniele Teti
R&D Director & Educational
What is DelphiMVCFramework?
• Powerful XE3, XE4 and XE5 framework for web
solutions
• Built in WebServer
• HTTP/HTTPS support
• It use Embarcadero WebBroker but is not tied to it
• Inspired to Java JAX-RS
• It is simple
• You can be productive in few minutes
1
25/11/2013
DMVCFramework main features
• RESTful
– Richardson Maturity Model Level 3
• Designed with services and web client app in mind
• Server side generated pages using eLua (Embedded
Lua)
• Can be used in load balanced environment using
memcached (memcached.org)
• Fancy URL with parameter mappings
• Integrated Delphi RESTClient
• Messaging extension using STOMP
• Experimental support for IOCP
What kind of applications can be build with
DMVCFramework?
• Application Servers
• RESTful web services
• Classic web application
• Web Client Applications
• Messaging solutions
– based on Apache ActiveMQ or Apache Apollo
• Delphi thin clients
• Mobile and Web backends
• Scalable (Load balanced) web systems
• Secure servers with HTTPS
2
25/11/2013
Simple architecture, multiple clients
DATABASE
Full Network, real world, many users
Reverse Proxy (nginx, Apache httpd, etc)
State Server1 State Server2
DATABASE
3
25/11/2013
Request/Response Cycle
Your first DMVCFramework
type
TWebModule1 = class(TWebModule)
procedure WebModuleCreate(Sender: TObject);
private
DMVC: TMVCEngine;
end;
. . .
procedure TWebModule1.WebModuleCreate(
Sender: TObject);
begin
DMVC := TMVCEngine.Create(self);
end;
4
25/11/2013
Richardson Maturity Model Level 3
DMVCFramework
http://martinfowler.com/articles/richardsonMaturityModel.html
Controllers, Actions and Routing
DelphiMVCFramework
5
25/11/2013
Architecture of DMVCFramework Server
• One Application
• Many Controllers
– Classes inherited from TMVCController
• Many Actions for each controller
– Controller Actions are its methods instrumented with a
specific attribute
• Controller is addresses using a piece of URL
• Actions are selected using the second part of the URL
Architecture of DMVCFramework Server
• One Application
• Many Controllers
– Classes inherited from TMVCController
• Many Actions for each controller
– Controller Actions are its methods instrumented with a
specific attribute
• Controller is addresses using a piece of URL
• Actions are selected using the second part of the URL
Server Name Controller Action
www.myserver.com/people/rome/danieleteti
6
25/11/2013
Controllers and Routing
type
[MVCPath(‘/blog’)]
TBlog = class(TMVCController)
public
[MVCPath(‘/posts/($year)/($month)/($title)’)]
procedure GetArticle(CTX: TWebContext);
end;
Matching URI Samples
/blog/posts/2011/05/a-brand-new-framework
/blog/posts/2013/05/rest-rest-for-delphi
/blog/posts/1/5/thisAndThat
/blog/posts/2013/05/123
DMVCFramework Attributes
Name Scope Optional Used for routing?
MVCPath Class No Yes
Method
MVCHTTPMethod Method Yes Yes
MVCConsumes Method Yes Yes
MVCProduces Method Yes No
7
25/11/2013
Controllers and Routing (1)
[MVCPath(‘/blog’)]
TBlog = class(TMVCController)
public
[MVCHttpMethod([httpGET])]
[MVCPath(‘/posts/($year)/($month)/($title)’)]
procedure GetArticle(CTX: TWebContext);
[MVCHttpMethod([httpPOST])]
[MVCPath(‘/posts/($year)/($month)/($title)’)]
procedure CreateArticle(CTX: TWebContext);
end;
Controllers and Routing (2)
[MVCPath(‘/blog’)]
TBlog = class(TMVCController)
public
[MVCHttpMethod([httpDelete])]
[MVCPath(‘/posts/($year)/($month)/($title)’)]
procedure DeleteArticle(CTX: TWebContext);
[MVCHttpMethod([httpPOST, httpPUT])]
[MVCPath(‘/posts/($year)/($month)/($title)’)]
procedure UpdateArticle(CTX: TWebContext);
end;
8
25/11/2013
Controllers and Routing (3)
[MVCPath(‘/blog’)]
TBlog = class(TMVCController)
public
[MVCProduce(‘application/json’, UTF8)]
[MVCHTTPMethod([httpGET])]
[MVCPath(‘/posts/($year)/($month)’)]
procedure GetArticleByMonth(
CTX: TWebContext);
end;
Controllers and Routing (4)
[MVCPath(‘/blog’)]
TBlog = class(TMVCController)
public
[MVCProduce(‘application/json’, UTF8)]
[MVCConsumes(‘application/json’)]
[MVCHTTPMethod([httpPOST])]
[MVCPath(‘/posts/($year)/($month)’)]
procedure CreateArticle(
CTX: TWebContext);
end;
9
25/11/2013
Request parameters
• Query String
– GET, POST, PUT, DELETE, HEAD, OPTIONS
• URL Mapped
– GET, POST, PUT, DELETE, HEAD, OPTIONS
• Request Body
– POST, PUT, OPTIONS
• Cookies
– GET, POST, PUT, DELETE, HEAD, OPTIONS
HTTP Safe Methods
• The convention has been established that the GET
and HEAD methods SHOULD NOT have the
significance of taking an action other than retrieval.
• These methods ought to be considered "safe".
– It is not possible to ensure that the server does not
generate side-effects as a result of performing a GET
request (eg. Logging, audit, tracing). However, the
important distinction here is that the user did not request
the side-effects, so therefore cannot be held accountable
for them.
10
25/11/2013
HTTP Idempotent Methods
• With IDEMPOTENT methods the side-effects of N > 0
identical requests is the same as for a single request.
• The methods GET, HEAD, PUT and DELETE share this
property. Also, the methods OPTIONS and TRACE
SHOULD NOT have side effects, and so are inherently
idempotent.
Reading Parameters
Context.Request.Params[‘ParamName’]
Reads Parameters in the following order
• URL Mapped parameters
• Query String parameters
• FORM parameters (eg. HTML Form Submit)
• Cookie fields
11
25/11/2013
URL mapped parameters
GET /blog/posts/danieleteti/2013/11
[MVCPath(‘/posts/($user)/($year)/($month)’)]
[MVCHTTPMethod([httpGET])]
procedure GetArticles (CTX: TWebContext);
. . .
procedure GetArticles (CTX: TWebContext);
var
year,month: Integer; user: String;
begin
user := CTX.Request.Params[‘user’];
year := CTX.Request.Params[‘year’].ToInteger;
month := CTX.Request.ParamsAsInteger[‘month’];
end
QueryString mapped parameters
GET /blog/posts/danieleteti?year=2013&month=11
[MVCPath(‘/posts/($user)’)]
[MVCHTTPMethod([httpGET])]
procedure GetArticles (CTX: TWebContext);
. . .
procedure GetArticles (CTX: TWebContext);
var
year,month: Integer; user: String;
begin
user := CTX.Request.Params[‘user’];
year := CTX.Request.Params[‘year’].ToInteger;
month := CTX.Request.Params[‘month’].AsInteger;
end
12
25/11/2013
DEMO
• Routing
Renders
DMVCFramework
13
25/11/2013
Using Renders
• «Renders» are a set of controller methods that actually
produces the response stream
• There are a lot of Render methods with oveload for many
types of data
• Renders can set the HTTP Status Code and the ContentType
• Currently you can render
– Text
– DataSets
– JSONValues
– Delphi Objects
– List of Delphi Objects
– Exceptions
– HTML pages using eLua
– Raw Streams
Render a TObject
procedure TPeople.GetPerson(CTX: TWebContext);
var
P: TPerson;
begin
P := TPerson.Create;
P.FirstName := 'Daniele';
P.LastName := 'Teti';
P.DOB := EncodeDate(1975, 5, 2);
P.Married := True;
Render(P);
end;
14
25/11/2013
Render a TJSONValue
procedure TPeople.GetPerson(CTX: TWebContext);
var
J: TJSONObject;
begin
J := TJSONObject.Create;
J.AddPair('FirstName', 'Daniele');
J.AddPair('LastName', 'Teti');
J.AddPair('DOB',
ISODateToString(EncodeDate(1975, 5, 2)));
J.AddPair('Married', TJSONTrue.Create);
Render(J);
end;
Render a TDataSet
procedure TCustomersController.GetAll(
CTX: TWebContext);
var
wm: TWebModule1; //the main WebModule
begin
wm := GetCurrentWebModule as TWebModule1;
wm.qryCustomers.Open;
//dataset is rendered as json array of objects
Render(wm.qryCustomers);
end;
15
25/11/2013
DEMO
• Renders
Filtering Action Requests
• Each action call (aka controller method) can
intercepted and handled
• Controllers initialization methods are also available
• DEMO
– ActionsFilter
16
25/11/2013
Session and Application Session
• DMVCFramework supports session for each request
• Session is a key/value structure private for each user
• Application Session is similar but it is shared to all users (WARNING!!!)
• Session is alive for Config[‘session_timeout’] minutes
procedure TTestServerController.Login(ctx: TWebContext);
begin
Session['username'] := ctx.Request.Params['username'];
end;
procedure TTestServerController.Logout(ctx: TWebContext);
begin
//destroy session with
SessionStop(false);
end;
Server Side Views with eLua
• Server side views uses the Lua language just like PHP
pages use PHP (code into text)
• eLua (Embedded Lua) has been specifically designed
for DMVCFramework in its brother project called
LuaDelphiBinding
• It is similar to PHP for HTML, JSP for Java and erb for
Ruby
<?lua= 3*8 ?> EXPRESSION
<?lua arbitrary_lua_code ?> SCRIPTLET
17
25/11/2013
Why Lua?
• Lua is small, fast and simple
– The whole implementation is less than 6000 lines of ANSI C
– Requires only one dll on Windows
• Has been designed as extension language
• Dynamic and not strongly typed
• Clear, simple and familiar syntax
• Great extensibility
• Functions as first-class values
• Tables a.k.a Associative arrays
• Garbage collection
• Allows extension of the semantics of the language.
• Widely used even outside the Delphi world
Is Lua popular?
• Lua is the most popular scripting language for game programming
• Adobe Photoshop Lightroom uses Lua for its user interface.
• Apache HTTP Server can use Lua anywhere in the request process
• Cisco uses Lua to implement Dynamic Access Policies within the Adaptive
Security Appliance.
• Damn Small Linux uses Lua to provide desktop-friendly interfaces
• FreePOPs, an extensible mail proxy, uses Lua to power its web front-end.
• MySQL Workbench uses Lua for its extensions & add-ons.
• Nginx has a powerful embedded Lua module that provides an API
• nmap network security scanner uses Lua as the basis for its scripting
language
• Vim has Lua scripting support
• VLC media player uses Lua to provide scripting support.
• Since March 2013, Lua is used as a new template scripting language on
Wikipedia and other Wikimedia Foundation wikis.
• WinGate proxy server allows event processing and policy to execute lua
scripts with access to internal WinGate objects.
• Wireshark network packet analyzer allows protocol dissectors and post-
dissector taps to be written in Lua
18
25/11/2013
eLua sample (expressions and code)
This is text but this is <?lua= ″Lua″ ?>
And now, Good <?lua
if isMorning() then
?>
Morning
<?lua
else
?>
Evening
<?lua
end
?> to all the people
Getting Started with eLua
• eLua files in the document_root are executed directly
• Actions can use eLua views using
LoadView(‘viewname’)
• Lua Helpers helps to create html page faster
• Similar to the RubyOnRails form helpers
19
25/11/2013
DEMO
• Simplewebapplication
Serve static files
• Use the «document_root» to serve static files
– HTML, Images, javascripts, binary…
• This allows to use Web Client javascript framework
– jQuery
– AngularJS
– Ember.js
– Backbone.js
– etc
20
25/11/2013
Web Client App DEMOS
• WineCellarServer
• WineCellarServerWITHDORM
• AngularJS\WebClientSample
Embedded Logging system
procedure Log(AMessage: string);
procedure LogW(AMessage: string);
procedure LogE(AMessage: string);
procedure LogEx(
AException: Exception;
AMessage: string);
procedure Log(LogLevel: TLogLevel;
const AMessage: string);
procedure LogEnterMethod(AMethodName: string);
procedure LogExitMethod(AMethodName: string);
Configurable LogLevel using Config
21
25/11/2013
Load Balancing
• DMVCFramework has been designed with clustering
in mind
• Session factory allows 2 kind of sessions
– Memory
• Do not support load balancing
• Sessions do not survives to restart
– State Server
• Support load balancing
• Sessions survive to restart
• Require a memcached daemon
Messaging estensions
• A specific system controller support Messaging
Extensions
• TMVCBUSController mapped to /messages
• Provides the following routes
[MVCPath('/subscribe/($name)')]
[MVCPath('/unsubscribe/($name)')]
[MVCPath('/receive')]
[MVCPath('/enqueue/($topic)')]
[MVCPath('/topics')]
• Method to enqueue message available
– EnqueueMessageOnTopic(topicname, jsonobject);
22
25/11/2013
Messaging Extensions DEMO
• CallbackDemo
RESTClient
DMVCFramework
23
25/11/2013
RESTClient
• Simple Delphi client for DMVCFramework
• Completely integrated with the Mapper
var
rest: TRESTClient;
response: IRESTResponse;
Person: TPerson;
begin
rest := TRESTClient.Create('localhost', 3000);
response := rest.doGET('/people', ['1']);
Person := Mapper.
JSONObjectToObject<TPerson>(response.BodyAsJSONObject);
ShowMessage(Person.FullName);
Person.Free;
rest.Free;
end;
RESTClient Asynch
RESTClient.Asynch(
procedure(Response: IRESTResponse)
begin
//do something with response
end,
procedure(E: Exception)
begin
//do something with exception
end).
doPOST('/echo', [‘one', ‘two'],
‘Hello World’);
24
25/11/2013
RESTClient Asynch
RESTClient.Asynch(
procedure(Response: IRESTResponse) begin
//do something with response
end,
procedure(E: Exception) begin
//do something with exception
end,
procedure begin
//when finished (success or failure)
end).
doPOST('/echo', [‘one', ‘two'],
‘Hello World’);
DMVCFramework SubProjects
DMVCFramework
25
25/11/2013
Mapper
• Powerful converter for Delphi Objects, JSONValues and
DataSets
• Some notable conversions
– ObjectToJSONObject
– JSONObjectToObject<T>
– ObjectListToJSONArray
– JSONArrayToObjectListOf<T>
– DataSetToJSONArray
– JSONArrayToObjectListOf<T>
LuaDelphiBinding
• Lua bind for Delphi
• Uses Lua 5.1
• Helper methods to push Delphi dictionaries and Delphi
Object as Lua Tables
• Helper to publish functions
• LuaTextFilter to handle eLua even outside the
DMVCFramework
26
25/11/2013
Thank You
Daniele Teti
d.teti@bittime.it
www.danieleteti.it
@danieleteti
27