WiRL Tutorial (ConsoleApp)

Luca Minuti - Feb 5 - - Dev Community

In questo tutorial vedremo il modo più semplice per sviluppare un server 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

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:

program Project1;

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

begin
  Readln;
end.
Enter fullscreen mode Exit fullscreen mode

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.

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.
Enter fullscreen mode Exit fullscreen mode

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 e GraphQL.

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

uses
  ...
  WiRL.Core.Engine,
  ...

begin
  ...
  LServer.AddEngine<TWiRLEngine>('rest');
  ...
Enter fullscreen mode Exit fullscreen mode

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 e 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.

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.
Enter fullscreen mode Exit fullscreen mode

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 presenti nei sorgenti si trovano numerosi esempi. Qui vedremo come restituire un semplice oggetto di tipo TPerson serializzato come JSON.

Incominciamo a definire la classe TPerson:

  TPerson = class(TObject)
  private
    FName: string;
  public
    property Name: string read FName write FName;
  end;
Enter fullscreen mode Exit fullscreen mode

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:

  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;
Enter fullscreen mode Exit fullscreen mode

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:

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

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:

TWiRLResourceRegistry.Instance.RegisterResource<TPersonResource>;
Enter fullscreen mode Exit fullscreen mode

E avviando il programma possiamo puntare il browser su:

http://localhost:8080/rest/app/person?name=luca
Enter fullscreen mode Exit fullscreen mode

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

Url

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 è possibile trovare ulteriori informazioni nella guida ufficiale e negli esempi presenti nei sorgenti.

. . . . . . . . . . . .