Implementazione di un server web completo

La lezione conclusiva del ciclo di lezioni introduttive a Node.js mostra come implementare un server HTTP applicando quanto discusso nelle varie lezioni.

Questo articolo conclude il ciclo di lezioni introduttive a Node.js. Un riassunto delle prime lezioni, dove abbiamo trattato i concetti fondamentali di Node.js è disponibile qui. Alcuni spunti su come organizzare il codice per gestire la concorrenza delle richieste HTTP sono discussi qui. Mettendo assieme i concetti fondamentali di Node.js coi criteri di design di un’architettura professionale, abbiamo dedotto una metodologia di sviluppo orientata agli eventi: per un esempio di architettura rimandiamo a questo articolo.

Nell’ultima lezione abbiamo affrontato la questione dello smistamento delle richieste HTTP, gettando le fondamenta del codice di un dispatcher implementato dalla classe HttpDispatcher. Il codice discusso nell’ultima lezione è corretto dal punto di vista strutturale, ma è incompleto dal punto di vista funzionale. Per ragioni didattiche, nell’ultima lezione abbiamo realizzato un dispatcher che registra i vari listeners, permette di analizzarli usando una funzione introspettiva, ma di fatto non svolge alcun smistamento delle richieste. Realizzare un dispatcher senza un’adeguata organizzazione della struttura dati sarebbe stato un po’ come mettere il carro davanti ai buoi: adesso che le fondamenta sono pronte, possiamo implementare il metodo che si occupa dello smistamento vero e proprio delle richieste HTTP.

Il codice che vedremo nelle prossime pagine va aggiunto, tramite prototpying, al codice della classe HttpDispatcher vista nell’ultima lezione.

Riassumiamo brevemente il nostro scopo: abbiamo creato una classe HttpDispatcher per implementare il dispatcher, che abbiamo salvato all’interno di un modulo che per comodità chiameremo “module-02.js” (per distinguerlo da altri moduli introdotti nelle lezioni precedenti). Dopodiché abbiamo creato un server, che chiameremo semplicemente “server.js”, il quale si occupa di lanciare il servizio HTTP di Node.js, delegando al dispatcher lo smistamento delle richieste. Vediamo adesso come completare il codice di questi due oggetti (modulo e server) per ottenere un’applicazione completa e funzionante.

Modulo e risorse

Iniziamo aggiungendo il codice principale del modulo “module-02.js” . In particolare vogliamo aggiungere un nuovo metodo alla classe HttpDispatcher. Questo metodo può essere aggiunto in due modi: o modifichiamo la definizione della classe, aggiungendo un’istruzione del tipo:

oppure possiamo aggiungere il metodo tramite prototyping. Questa è probabilmente la soluzione migliore dal punto di vista della manutenzione del codice, perché rende semplice e leggibile lo sviluppo incrementale da una classe: invece di modificare la classe originale, aggiungiamo del codice “a sé stante”:

se abbiamo ben compreso il funzionamento della struttura dati discussa nell’ultima lezione, il funzionamento del metodo dovrebbe essere evidente: dopo aver eseguito il parsing dell’URL richiesto al server non facciamo altro che cercare il giusto listeners all’interno della struttura che abbiamo chiamato list. Per ulteriori dettagli rimandiamo alla lezione precedente.

Lo stratagemma permette di inoltrare l’azione a qualunque callback definita dal server, senza dover modificare il codice del modulo (il dispatcher). In questo modo il carico di lavoro (dal punto di vista dello sviluppatore) viene trasferito nella configurazione del server, come vedremo nella prossima pagina.

Prima di affrontare il codice del server creiamo una risorsa HTML che ci tornerà utile quando l’utente accede alla radice (root) del server. Copiamo il codice qui sotto all’interno di un file che chiameremo “home.html”:

Il server completo

A questo punto abbiamo completato il codice del modulo “module-02.js” (la classe HttpDispatcher) e abbiamo a disposizione una risorsa HTML da servire all’utente quando viene richiesta la root del server. Per completare l’applicazione resta solo da aggiungere il codice che registra i listeners (che verranno gestiti dal dispatcher), e poi eseguire il server principale. Per comodità riportiamo qui sotto il codice completo del server:

come accennato, la parte “più noiosa” è la registrazione dei listeners. Qui sopra abbiamo scelto di registrare i listeners uno alla volta. In un’applicazione professionale potremmo leggere la lista dei listeners da una risorsa (ad esempio un file INI) e poi registrare i vari listeners usando un ciclo for. Una modifica di questo tipo può essere un ottimo esercizio.

Notiamo che le richieste alla root del server sono esaudite da un listeners che accede al file system, restituendo la risorsa home.html di pagina precedente. Questa non è l’unica scelta possibile, ma spesso risulta comoda. Il codice che accede al file system potrebbe essere modificato per gestire un intero albero di directories, eseguendo le opportune operazioni di analisi sul pattern dell’URL richiesto al server.

Per eseguire l’applicazione collochiamo tutte le risorse (server.js, module-02.js e home.html) nella stessa directory, dopodiché eseguiamo il server come al solito (cioè node server.js). Aprendo il browser all’indirizzo specificato, che nel nostro caso sarà http://localhost:8080, dovremo trovare la pagina HTML che abbiamo configurato come home. Da qui possiamo navigare i vari link per testare gli URL gestiti dal dispatcher, sia di tipo HTTP GET che di tipo HTTP POST.

Il prodotto finale utilizza un pattern che possiamo utilizzare anche per applicazioni professionali. Chiaramente è necessario migliorare gli aspetti che abbiamo già evidenziato, che ripetiamo: registrare i listeners in modo automatico (leggendoli da un file di configurazione); migliorare il metodo che accede al file system (per poter servire tutte le risorse che vogliamo rendere accessibili); aggiungere il codice di gestione degli errori (risorsa mancante o listeners non trovato). Tutte queste modifiche andrebbero apportate rispettando il pattern modulare, ottenendo due vantaggi: un’applicazione più facile da mantenere e la possibilità di centralizzare (e riutilizzare) il codice.

Facci sapere cosa ne pensi!

  1. Liciof says

    Ottima guida; ho letto tutte le lezioni e sono fatte proprio bene, complimenti all’autore!
    Ho testato il codice è funziona perfettamente anche sulla versione 6.11.3 di node.

  2. Liciof says

    Per Pierpaolo posso dire che i test li ho fatti installando node.js versione 6.11.3 su windows 7 Home Premium service pack 1.

Facci sapere cosa ne pensi!

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *