WiRL Tutorial (ConsoleApp)
Tags: delphi, rest
Published: 2024-02-05 08:16:51

In questo tutorial vedremo il modo più semplice per sviluppare un server [WiRL](https://github.com/delphi-blocks/WiRL). In realtà se durante l'installazione avete installato il package designtime `WiRLDesign.dproj` in Delphi, alla voce *File|New|Other*, avrete una nuova opzione "WiRL Server Application Wizard" che crea una scheletro per una applicazione WiRL. 

![WiRL Server Application Wizard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/552h81vhq9vbgfp3jde5.png)

Ma qui vedremo passo passo come configurare il vostro server manualmente in modo da avere un'idea più chiara di quello che succede.


## Applicazione console

Cominciamo creando un'applicazione console che può andare bene per lo sviluppo ed eventualmente può essere facilmente trasformata in un servizio Windows per l'ambiente di produzione.

Riduciamo all'osso il codice e andiamo a trasformare quello generato da Delphi in questo:

```pascal
program Project1;

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

begin
  Readln;
end.
```
In questo modo abbiamo un'applicazione che apre una console e termina alla pressione del tasto invio.

## Creazione del server

Per prima cosa dovremo andare a creare un oggetto di tipo `TWiRLServer` che si occuperà di rimanere in ascolto su una specifica porta e che smisterà il traffico HTTP. Se provate ad attivare il server (tramite la proprietà `Active`) riceverete un errore a runtime:

> Project Project1.exe raised exception class EWiRLException with message 'CreateServer: no server registered (add "WiRL.http.Server.*" unit to the project)'.

Questo perché la struttura di TWiRLServer gli permette di aprire il canale HTTP tramite diverse librerie; e non abbiamo ancora indicato quale libreria usare. In questo caso utilizzeremo i componenti Indy (già installati in Delphi). Per farlo è sufficiente aggiungere la unit `WiRL.http.Server.Indy`.

```pascal
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.
``` 

## Configurare l'engine

Con il codice che abbiamo scritto abbiamo già un server HTTP funzionante ma qualsiasi tentativo di connessione restituirà l'errore:

> Project Project1.exe raised exception class EWiRLNotFoundException with message 'Engine not found for URL [/test]'. 

Questo perché non abbiamo ancora configurato l'**Engine**. L'engine è la parte di WiRL che si occupa di interpretare la chiamata. Al momento esistono due engine: `TWiRLEngine` e `TWiRLFileSystemEngine`. Il primo è il motore principale di WIRL che si occupa di gestire le chiamate ReST ed è quello che vedremo in questo tutorial, mentre il secondo permette di restituire file (html, js, css, ecc.). Volendo è possibile implementare degli Engine custom per esempio per gestire chiamate [SOAP](https://en.wikipedia.org/wiki/SOAP) e [GraphQL](https://graphql.org/).

Ogni engine è associato ad un path quindi per usare l'engine ReST possiamo usare un codice simile a questo:

```pascal
uses
  ...
  WiRL.Core.Engine,
  ...

begin
  ...
  LServer.AddEngine<TWiRLEngine>('rest');
  ...
```

## Configurare l'applicazione

A questo punto è possibile configurare i diversi moduli di WiRL, per esempio:

* Le risorse 
* I filtri
* I message body writer e read
* Le librerie esterne (es. [neon](https://github.com/paolo-rossi/delphi-neon) e [JWT](https://github.com/paolo-rossi/delphi-jose-jwt))

WiRL ha un concetto di *applicazione virtuale*. In questo modo è possibile in un'unica applicazione fisica creare più moduli con delle configurazioni diverse. In questo caso, per semplicità useremo solo un'applicazione virtuale con la configurazione minima.

```pascal
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;

    LServer.AddEngine<TWiRLEngine>('rest')
      .AddApplication('app')
      .SetResources('*');

    Writeln('Server running at http://localhost:' + LServer.Port.ToString + '/');
    Readln;
  finally
    LServer.Free;
  end;
end.
```
In questo modo andiamo a creare l'applicazione *app*, raggiungibile tramite il path omonimo, contenente tutte le risorse.

Con questo abbiamo terminato la configurazione ma mancano ancora le risorse.

## La prima risorsa

WiRL è in grado di manipolare (sia in ingresso che in uscita) diversi tipi di dati, da quelli più semplici, come stringhe o interi, a oggetti complessi compresi anche i DataSet. Questi saranno le nostre *risorse* ReST. Nelle [demo](https://github.com/delphi-blocks/WiRL/tree/master/Demos) presenti nei sorgenti si trovano numerosi esempi. Qui vedremo come restituire un semplice oggetto di tipo `TPerson` serializzato come [JSON](https://www.json.org/json-it.html).

Incominciamo a definire la classe `TPerson`:

```pascal
  TPerson = class(TObject)
  private
    FName: string;
  public
    property Name: string read FName write FName;
  end;
```

A questo punto dobbiamo creare una classe che sia in grado di manipolare l'oggetto `TPerson`. In particolare proviamo a creare una semplice classe che istanzia oggetti:

```pascal
  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;
```

Quello che abbiamo visto è una normalissima classe con un metodo `GetPerson` che crea le nostre persone. Non ci rimane che spiegare a WiRL come usare la classe `TPersonResource` per rispondere alla chiamate HTTP. *WiRL* ci permette di fare tutto ciò decorando la classe e i metodi con degli attributi:

```pascal
  [Path('person')]
  TPersonResource = class
  public
    [GET]
    [Produces(TMediaType.APPLICATION_JSON)]
    function GetPerson([QueryParam('name')] const AName: string): TPerson;
  end;
```

Gli attributi (dichiarati nella unit `WiRL.Core.Attributes`) che abbiamo usato sono:

* `Path`: Applicato ad una classe. Indica il path a cui la risorsa risponde. In pratica quando arriva una chiamata ReST con quel path WiRL istanzierà la classe e la distruggerà al termine della chiamata. Puoi essere anche applicato ad un metodo ed in quel caso indica una "sotto risorsa", il path del metodo e quello della classe verranno usati insieme per determinare l'URL della sotto risorsa.
* `GET`: Applicato ad un metodo della classe. Quel metodo di istanza sarà chiamato quando il metodo HTTP corrisponde a *GET* (esistono anche gli attributi `POST`, `PUT`, `DELETE`, ecc).
* `Produce`: Applicato ad un metodo. Indica che quel metodo deve essere chiamato SOLO se il client richiede il media type indicato (header `Accept` della richiesta). Esiste anche l'attributo `Consumes` che serve quando con la richiesta arrivano anche dei dati nel corpo del messaggio HTTP. In quel caso `Consumes` indica se il nostro metodo è in grado di accettare quel tipo di contenuto.
* `QueryParam`: Viene usato su un parametro della richiesta. In questo caso indica che il parametro deve essere letto dalla *query string* indicata nell'URL. Esistono anche `PathParam`, `FormParam`, `BodyParam` e molti altri.

A questo punto dobbiamo solo registrare la nostra risorsa:

```pascal
TWiRLResourceRegistry.Instance.RegisterResource<TPersonResource>;
```

E avviando il programma possiamo puntare il browser su:

```http
http://localhost:8080/rest/app/person?name=luca
```

Riceveremo il JSON corrispondente alla classe `TPerson`. Per capire l'URL corretto da usare possiamo attenerci a questo schema:

![Url](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2jw5q40vksh0sajt6cxe.png)

Dove sono indicate le varie sezioni dell'URL e chi ne è responsabile:

* `TWiRLServer`: imposta la porta col metodo `SetPort` (*8080*)
* `TWiRLEngine`: per la prima parte dell'URL con `AddEngine` (*rest*)
* `TWiRLApplcation`: tramite il metodo `AddApplication` (*app*)
* La risorsa: tramite l'attributo `Path` (person)

## Conclusioni

Con questo abbiamo concluso il nostro esempio, il codice completo si trova su [GitHub](https://github.com/lminuti/WiRLDemos/blob/master/01/WiRLDemo01.dpr) è possibile trovare ulteriori informazioni nella [guida ufficiale](https://github.com/delphi-blocks/WiRL/wiki) e negli [esempi](https://github.com/delphi-blocks/WiRL/tree/master/Demos) presenti nei sorgenti.