Getting started with WiRL Server
In this tutorial we will see the simplest way to develop a WiRL server. If during installation you installed the designtime package WiRLDesign.dproj
in Delphi, under File|New|Other, you will have a new option "WiRL Server Application Wizard" which creates a skeleton for a WiRL application.
But now we will see a step by step guide on how to configure your server manually in order to have a better understanding of WiRL internal.
Console application
Let's start by creating a console application, it can be good choice for development and eventually can be easily transformed into a Windows service for the production environment.
Let's reduce the code to the bare bones and transform the one generated by Delphi into this:
program Project1;
{$APPTYPE CONSOLE}
uses
System.SysUtils;
begin
Readln;
end.
Now we have an application that opens a console and ends when the enter key is pressed.
Creating the server
First we will have to create a TWiRLServer
object which will listen on a specific port and which will route the HTTP traffic. If you try to activate the server (via the property Active
) you will receive an error at runtime:
Project Project1.exe raised exception class EWiRLException with message 'CreateServer: no server registered (add "WiRL.http.Server.*" unit to the project)'.
This is because the structure of TWiRLServer allows it to open the HTTP channel via different libraries; and we have not yet indicated which library to use. In this case we will use the Indy components (already installed in Delphi). To do this, simply add the unit WiRL.http.Server.Indy
.
program Project1;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
WiRL.http.Server.Indy,
WiRL.http.Server;
var
LServer: TWiRLServer;
begin
LServer := TWiRLServer.Create(nil);
try
LServer.SetPort(8080);
LServer.Active := True;
Writeln('Server running at http://localhost:' + LServer.Port.ToString + '/');
Readln;
finally
LServer.Free;
end;
end.
Configure the engine
With the code we have already written we have a working HTTP server but any connection attempt will return the error:
Project Project1.exe raised exception class EWiRLNotFoundException with message 'Engine not found for URL [/test]'.
This is because we haven't configured the Engine yet. The engine is the WiRL module that is responsible for interpreting the call. There are currently two engines: TWiRLEngine
and TWiRLFileSystemEngine
. The first is the main WIRL engine that handles ReST calls and is what we will see in this tutorial, while the second allows you to return files (html, js, css, etc.). It is also possible to implement custom Engines, for example to manage SOAP and GraphQL calls .
Each engine is associated with a path, in this way WiRL is able to understand the engine to use through the requested URL with a code similar to this:
uses
...
WiRL.Core.Engine,
...
begin
...
// http://localhost:8080/rest/...
LServer.AddEngine<TWiRLEngine>('rest');
...
Configure the application
Then it's possible to configure the different WiRL modules, for example:
WiRL has a virtual application concept. In this way it is possible to create multiple modules with different configurations in a single physical application. In this case, for the sake of simplicity, we will only use one virtual application with the minimum configuration.
program Project1;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
WiRL.http.Server.Indy,
WiRL.http.Server,
WiRL.Core.Engine;
var
LServer: TWiRLServer;
begin
LServer := TWiRLServer.Create(nil);
try
LServer.SetPort(8080);
LServer.Active := True;
// http://localhost:8080/rest/...
LServer.AddEngine<TWiRLEngine>('rest')
// http://localhost:8080/rest/app/...
.AddApplication('app')
.SetResources('*');
Writeln('Server running at http://localhost:' + LServer.Port.ToString + '/');
Readln;
finally
LServer.Free;
end;
end.
In this way we are going to create the application named app, reachable via the path of the same name, containing all the resources.
With this we have finished the configuration but the resources are still missing.
The first resource
WiRL is able to handle (both input and output) different types of data, from the simplest ones, such as strings or integers, to complex objects including streams, collections, DataSets and so on. These will be our ReST resources. Numerous examples can be found in the demos present in the sources. Here we will see how to return a simple object like TPerson
as JSON.
Let's start by defining the class TPerson
:
TPerson = class(TObject)
private
FName: string;
public
property Name: string read FName write FName;
end;
Now we need to create a class that can create the TPerson
object. In a more complex application this class would probably read the object from a database or something similar. The class should also be responsible for modifying the object. But for this example we will only create the object initialized by the method arguments:
TPersonResource = class
public
function GetPerson(const AName: string): TPerson;
end;
function TPersonResource.GetPerson(const AName: string): TPerson;
begin
Result := TPerson.Create;
Result.Name := AName;
end;
What we see is a very basic class with a method GetPerson
that creates our person. All we have to do is explain to WiRL how to use the class TPersonResource
to respond to HTTP calls. WiRL allows us to do this by decorating the class and methods with attributes:
[Path('person')]
TPersonResource = class
public
[GET]
[Produces(TMediaType.APPLICATION_JSON)]
function GetPerson([QueryParam('name')] const AName: string): TPerson;
end;
The attributes (declared in the unit WiRL.Core.Attributes
) are:
Path
: (Applied to a class o method) The path of the resource. When a ReST call arrives with that path WiRL will instantiate the class and destroy it at the end of the call. It can also be applied to a method and in that case it indicates a "sub-resource". The method path and the class path will be used together to get the URL of the sub-resource.GET
: (Applied to a method of the class) That instance method will be called when it matches with the HTTP method (others HTTP method attributes arePUT
,POST
,DELETE
and so on).Produce
: (Applied to a method) WiRL call the method ONLY if the client requests the right media type (Accept
request header). There is also the attributeConsumes
that is needed when data also arrives in the body of the HTTP request. In that caseConsumes
it indicates if our method is able to accept that type of content.QueryParam
: It is used on a request parameter. In this case it indicates that the parameter must be read from the query string indicated in the URL. There are alsoPathParam
,FormParam
,BodyParam
and many others.
Now we need to register our resource:
TWiRLResourceRegistry.Instance.RegisterResource<TPersonResource>;
And by starting the program we can point the browser to:
http://localhost:8080/rest/app/person?name=luca
We will receive the JSON corresponding to the class TPerson. To understand the correct URL to use we can follow this scheme:
Here a summary of the classes involved in the URL parsing:
TWiRLServer
: set the port with the method SetPort( 8080 )TWiRLEngine
: for the first part of the URL with AddEngine( rest )TWiRLApplcation
: via method AddApplication( app )- The resource: via the
Path
(person) attribute
Conclusions
With this we have our first example up and running.