martedì, dicembre 23, 2014

Auguri!!!

Da domani sarò in vacanza per un paio di settimane e non credo riuscirò a scrivere altri post prima del prossimo anno :)

Auguri di cuore a tutti voi.


Alla prox.
Ivan

lunedì, dicembre 22, 2014

Iterazioni facili facili con jade

Un breve post solo per mostrare come si itera una collezione di oggetti da un template jade.

Spesso ci troviano nella condizione di dover mostrare dati dinamici in forma tabellare.
Con express e jade è molto semplice: basta preparare un array di oggetti e renderlo disponibile al template jade.

Ho aggiornatato l'applicazione su https://whispering-peak-1284.herokuapp.com/, il primo link del menù rimanda ad una pagina che visualizza una tabella di libri preparata nella funzione di callback che gestisce la richiesta: in questo caso ho cablato l'array nel codice, in futuro vedremo come recuperarlo da una base dati
 app.get('/pagina1', function(req,res) {  
      res.render('pagina1', {libri: [{  
           titolo: 'Il signore degli anelli',  
           autore: 'Tolkien'  
      },{  
           titolo: 'Guerra e pace',  
           autore: 'Tolstoj'  
      }, {  
           titolo: 'Javascript the good parts',  
           autore: 'Crockford'  
      }]});   
 });   
Basta passare, nel metodo render, un oggetto javascript con le prorprietà che vogliamo mettere a disposizine per il template jade.

Nel template jade, vi ricordo di spulciare la documentazione per i dettagli, si usa il costrutto each
 block contenuti-principali  
        ...
        p.text-info  
           if (libri)  
                table(class='table table-striped')  
                     each libro in libri  
                          tr  
                               <td>#{libro.titolo}</td>  
                               <td>#{libro.autore}</td>  
           else  
                p.text-info Non ci sono libri in biblioteca
        ...
Testiamo la presenza di una proprietà libri e se c'è generiamo le righe dinamiche della tabella con il costrutto each oggetto in nomeaarray

Ho aggiornato su github il progetto installato su heroku, in modo che possiate avere tutto il codice a disposizione, in questo post è spiegato come utilizzare github.

Fate il pull del progetto, se volete eseguire il programma in locale digitate node app.js e puntate il browser all'indirizzo http://localhost:5000.

Alla prox
Ivan

sabato, dicembre 20, 2014

Gestione della sessione con express

Ogni applicazione web che si rispetti, deve saper gestire i dati di sessione.

Il protocollo http è stateless per natura: ogni richiesta http è indipendente dalle altre, per sviluppare applicazioni web dobbiamo essere in grado di capire quando una determinata richiesta appartiene alla sessione di comunicazione valida di un particolare client.

Il classico esempio per capire il concetto di dato di sessione è il carrello della spesa di un sito e-commerce: con una richiesta http inseriamo un prodotto nel carrello, con una seconda, indipendente richiesta, ne inseriamo un altro e così via: dobbiamo garantire  per ogni utente che sta acquistando, un proprio carrello della spesa!

Per gestire una sessione lato server, di solito si usa un meccanismo del genere:
  1. Alla prima richiesta di un client si genera un id (un numero intero) univoco per rappresentare la sessione
  2. Tramite il meccanismo dei cookies o url rewriting, si obblica il client a mandare con le successive richieste l'id della sessione, in modo da permettere al server di capire a quale sessione appartiene una determinata richiesta.
  3. Quando, per una determinata sessione, non arrivano più richieste entro un determinato intervallo di tempo (ad esempio 20 minuti), la sessione è considerata scaduta e tutti i dati relativi vengono cancellati

Nella figura, alla prima richiesta il client manda un parametro al server, che crea un id di sessione e memorizza il valore del parametro come dato di sessione: nella prima risposta il server dice al client di memorizzare l'ID 12345 in un cookie.

Il client, con seconda richiesta, racchiude l'informazione dell'ID in modo che il server possa reperire i dati della sessione corrispondente a quell'identificatore.

Normalmente i dati di sessione sono salvati in memoria: se si riavvia il server i dati delle sessioni eventualmente attive vengono persi.

Vediamo come memorizzare e ottenere dati dalla sessione utilizzando express.

Dobbiamo utilizzare solo un paio di middleware e saremo in grado di accedere ai dati della sessione, questi middleware non sono inclusi nella distribuzione standard di express, dovremo installarli con npm con i seguenti comandi dalla cartella root del progetto

  • npm install cookie-parser --save
  • npm install express-session --save
L'opzione --save serve ad aggiungere le nuove dipendenze nel file package.json.

Nel file app.js, aggiungiamo le seguenti istruzioni per includere ed utilizzare i middleware necessari
 var express = require('express');  
 var cookieParser = require('cookie-parser');  
 var expressSession = require('express-session');  
   
 var app = express();   
   
 ...  
   
 app.use(cookieParser());  
   
 app.use(expressSession({  
      secret: 'stringaqualunque',  
      resave: false,  
      saveUninitialized: true    
 }));   
   
 app.use(function(req,res,next) {  
      res.locals.session = req.session;  
      next();   
 });  
L'ultimo use() serve a rendere disponibile al motore jade l'oggetto session che sarà sempre disponibile in ogni oggetto request.

Ho modificato l'applicazione su heroku https://whispering-peak-1284.herokuapp.com/ , nel file layout.jade ho inserito un codice condizionale di questo tipo:
 div.page-header  
                if (session.utente)  
                     | Benvenuto #{session.utente}  
                else  
                     | Non hai ancora inserito un   
                     | nome per il saluto! Appena   
                     | lo farai verrà messo in sessione  
Nella divisione con classe page-header, testo la presenza di un oggetto utente in sessione:  lo possiamo fare solo grazie alla definizione dell'ultimo middleware visto in precedenza.

Quando si naviga il sito la prima volta e prima di utilizzare il form, comparirà il messaggio del ramo else, dopo aver usato il form, verrà creata una proprietà utente nell'oggetto sessione valorizzata con il parametro che arriva al submit del form: da questo momento in avanti, anche facendo altre richieste, comparirà sempre il messaggio del ramo if, che va a prelevare il dato dalla sessione.

Il codice per memorizzare il nome in sessione al submit del form è semplicemente il seguente:
 app.get('/benvenuto', function(req,res){  
      req.session.utente = req.query.nome;  
      res.render('benvenuto', {nomeCognome : req.query.nome});  
 });   
Notate bene: senza cookie-parser e express-session non avremmo un oggetto session come proprietà della request.

Ho aggiornato su github il progetto installato su heroku, in modo che possiate avere tutto il codice a disposizione, in questo post è spiegato come utilizzare github.

Dopo aver fatto il pull del progetto, ricordatevi di eseguire npm install nella cartella di progetto per installare le dipendenze aggiuntive: se volete eseguire il programma in locale digitate node app.js e puntate il browser all'indirizzo http://localhost:5000.

Alla prox
Ivan

venerdì, dicembre 19, 2014

Modelli di pagina con Jade

Date un'occhiata all'indirizzo della applicazione che stiamo sviluppando https://whispering-peak-1284.herokuapp.com/ : notato niente di nuovo?

L'occhio vuole sempre la sua parte, con questo post vediamo come implementare un modello per il nostro sito che va mantenuto costante per ogni pagina presente.

In questo jade ci da una grossa mano, perchè ci permette di definire dei veri e propri file modello che le singole pagine possono estendere.

Per il disegno del modello, ho deciso di utilizzare bootstrap, un file css pronto all'uso che ci semplifica enormemente la vita.

Il layout (modello) che vogliamo utilizzare per il nostro sito è il seguente


Ho evidenziato le varie parti del layout costituito da un header, una colonna di sinistra per la navigazione, una colonna centrale per i contenuti principali, la colonna di destra e un footer in basso.

Con bootstrap e il suo sistema a griglia è veramente semplice definire un layout del genere
 <div class="page-header">Header</div>  
 <div class="container-fluid">  
    <div class="row">  
       <div class="col-md-2">Colonna sinistra</div>  
       <div class="col-md-6">Colonna Centrale</div>  
       <div class="col-md-4">Cononna Destra</div>  
    </div>  
    <div class="row">  
       <div class="col-md-12">Footer</div>  
    </div>  
 </div>  
Basta usare una divisione che fa da contenitore e inserire altre divisioni di tipo riga che contengono divisioni di tipo colonna: ricordatevi semplicemente che la somma dello spazio totale delle divisioni all'interno di una riga deve fare 12.

La forza di bootstrap sta anche nell'adattare automaticamente il layout in funzione della tipo di display che sta navigando la pagina, implementa cioè quello che in gergo si chiama responsive design.

Ogni pagina del sito dovrà rispettare questo layout, le parti varibili saranno rappresentate dal contenuto dell'header e le varie colonne.

Creiamo ora un file layout.jade che verrà esteso dalle altre pagine in modo da fattorizzare le parti comuni del modello scelto
 doctype html  
 html(lang='it')  
      head  
           meta(charset='utf-8')  
           meta(http-equiv='X-UA-Compatible', content='IE=edge')  
           meta(name='viewport', content='width=device-width, initial-scale=1')  
   
           block titolo  
                title Sito node.js + express  
           link(rel='stylesheet', href='/css/bootstrap.css')  
           link(rel='stylesheet', href='/css/bootstrap-theme.css')  
           link(rel='stylesheet', href='/css/site.css')  
      body  
           div.page-header  
                block header
           div.container-fluid  
                div.row  
                     div.col-md-2  
                          block colonna-sinistra  
                               include ./menu.jade  
                     div.col-md-6  
                          block colonna-centrale
                     div.col-md-4  
                          block colonna-destra  
                                 
                div.row  
                     div.col-md-12  
                          block footer  
                               hr  
                               p(class='text-center') &copy; Ivan Saracino  
                                 
           script(src='/js/jquery.js')  
           script(src='/js/bootstrap.js')  
Bootstrap è costituito da due file css e due file javascript che vanno messi nella sezione /public/css e /public/js del nostro progetto.

Notate la presenza dell'istruzione block nome-blocco, ogni pagina che estenderà questo modello dovrà poi definire i contenuti dei vari blocchi in esso definiti: se non lo fa, verranno usati i blocchi di default come quello usato per il footer.

Notate la presenza dell'istruzione include ./menu,jade inserita come blocco di default per la colonna di sinistra, serve semplicemente ad includere i contenuti presenti in un altro file in cui è definito il menu di navigazione.
 ul(class='nav nav-pils')  
      li(role="presentation",class="active")  
           a(href="/") Home  
      li(role="presentation")  
           a(href="/pagina1") Pagina 1  
      li(role="presentation")  
           a(href="/pagina2") Pagina 2  
Tutte le classi css che ho utilizzato nell'esempio sono fornite da bootstrap, se utilizzate sublime text 3, come spiegato in questo post, installate il package Bootstrap 3 jade snippets per scrivere al volo frammenti jade/bootstrap per gli usi più comuni. (esempio il menu di navigazione ;))

Vediamo ora l'esempio di una delle pagine, esempio pagina1.jade, che estende il nostro layout
 extends ./layout.jade  
   
 block titolo  
      title Titolo particola per questa pagina  
   
 block header  
      h1.text-info Header particolare per questa pagina  
   
 block colonna-centrale  
      p contenuto colonnna centrale per questa pagina  
   
 block colonna-destra  
      p contenuto colonna destra per questa pagina  
Vengono semplicemente definiti i contenuti dei vari blocchi del layout che estendiamo, se non definiamo un blocco, come ad esempio colonna-sinistra e footer, vengono usati i blocchi di default presenti nel layout.

Ho messo su github il progetto completo che ho installato su heroku, in modo che possiate avere tutto il codice a disposizione, in questo post è spiegato come utilizzare github.

Dopo aver fatto il pull del progetto, ricordatevi di eseguire npm install nella cartella di progetto per installare le dipendenze express e jade: se volete eseguire il programma in locale digitate node app.js e puntate il browser all'indirizzo http://localhost:5000.

Alla prox
Ivan

giovedì, dicembre 18, 2014

Sutor, ne supra crepidam!

Calzolaio, non più in alto della scarpa! Pare che fosse stata la risposta data da Apelle ad un calzolaio che, dopo aver criticato una calzatura di un suo quadro, aveva cominciato a criticarne anche altre parti nonostante la sua incompetenza.

Quando sviluppiamo applicazioni per il web, è bene suddividere il nostro codice tra componenti che si occupano esclusivamente della logica e dei dati dell'applicazione e componenti che si preoccupano esclusivamente di presentare i dati all'utente finale.

In questo post avevamo visto come sia scomodo costruire la risposta in html direttamente dalla funzione di callback che gestisce la richiesta.

Express ci mette a disposizione il concetto di template engine, un componente che ha proprio il compito di creare la risposta finale (View) a partire da dati (Model) preparati dal gestore della richiesta (Controller), in modo da attuare quella separazione di responsabilità tanto cara ai bravi architetti informatici: MVC vi dice niente?

Express è molto flessibile, ci permette di scegliere differenti motori di renderizzazione delle viste, uno dei più diffusi è jade: in questo post vederemo come utilizzarlo e impareremo via via ad apprezzarne la semplicità di utilizzo.

Invece di creare direttamente delle pagine html, creeremo delle pagine simil html da dare in pasto a jade che si preoccuperà di compilare le parti dinamiche e restituire il codice html finale.

Vediamo ora come creare una pagina jade per dinamicizzare una risposta
 doctype html  
 html  
      head  
           meta(charset='UTF-8')  
      body  
           p Applicazione node.js su heroku ok!  
           p Benvenuto #{nomeCognome}   
La sintassi è molto intuitiva, si tratta di un simil html, dove ogni prima parola di una riga rappresenta un nodo html che verrà poi chiuso automaticamente da jade: molto importante l'utilizzo dell'identazione per indicare a jade la presenza dei sottonodi.

Eventuali attributi di un nodo si esprimono mettendo tra parentesi tonde la coppia nome=valore subito dopo il nome del nodo (guardate il nodo meta con il suo attributo charset ).

Le parti dinamiche si inseriscono utilizzando la notazione #{nomeVariabile}, dove nomeVariabile deve essere una proprietà di un oggetto javascript preparato dal controller.

Vediamo come configurare la nostra applicazione per l'utilizzo di jade come template engine.

Aggiungiamo la dipendenza a jade nel file package.json modificando la proprietà dependencies in questo modo
  "dependencies": {  
      "express": "~4.9.x",  
      "jade": "*"  
  },  
Aggiungiamo al progetto una cartella /views che conterrà tutti i file .jade

Modifichiamo app.js in questo modo
 var express = require('express');  
 var app = express();  
   
 app.set('port', (process.env.PORT || 5000));  
 app.set('views', __dirname + '/views');  
 app.set('view engine', 'jade');    
 app.use(express.static(__dirname + '/public'));   
   
 app.get('/benvenuto', function(req,res){  
      res.render('benvenuto', {nomeCognome : req.query.nome});  
 });  
   
 app.listen(app.get('port'), function() {  
  console.log("Node app is running at localhost:" + app.get('port'));  
 });  
Configuriamo il template engine per l'utilizzo di jade tramite le due istruzioni app.set(...), che specificano la cartella in cui sono presenti le viste e il tipo di motore, in questo caso jade.

Configuriamo poi la rotta /benvenuto  con una funzione di callback che prepara i dati, in questo caso un oggetto js {nomeCognome: req,query.nome} che verra passato a jade tramite l'istruzione res.render('benvenuto', {nomeCognome : req.query.nome}): il template engine andrà a cercare la pagina benvenuto.jade presente in /views e la compilerà sostituendo tutte le espressioni dinamiche con i relativi dati, in questo caso #{nomeCognome} sarà sostituito con il valore del parametro della richiesta che si chiama nome.

Ho modificato la pagina statica index.html in questo modo:
 <!DOCTYPE html>  
 <html lang="en">  
 <head>  
      <meta charset="UTF-8">  
      <title>Node js</title>  
 </head>  
 <body>  
      <p>Applicazione node.js su heroku ok!</p>  
      <form action="benvenuto">  
           Nome: <input type="text" name="nome" />  
              <input type="submit" value="Saluta!" />  
      </form>  
 </body>  
 </html>  
Ho aggiunto un form, in cui inserire un nome: al submit del form verrà effettuata una richiesta alla risorsa /benvenuto?nome=ValoreImmesso

D'ora in avanti dunque, in /public ci saranno solo risorse statiche, mentre in /views ci saranno le pagine dinamiche scritte in jade.

Provate a effettuare queste modifiche e a reinstallare il tutto su heroku, vi riassumo i passi da fare dopo che avete effettuato le modifiche

  1. git add --all
  2. git commit -m "introduzione template engine jade"
  3. git push heroku master
  4. heroku open
Se invece volete eseguire in locale:
  1. npm install (che scaricherà le dipendenze express e jade)
  2. node app.js
Su heroku è presente l'applicazione completa.

Alla prox.
Ivan

"Installiamo" le nostre web app node.js su Heroku!

Prima di proseguire, affrontiamo il problema di rendere accessibile la nostra webapp al mondo intero senza spendere una lira :)

Io utilizzo da un pò la piattaforma heroku, ideale per piccole applicazioni di sviluppo che non necessitano di particolari risorse: se poi un giorno la vostra applicazione dovesse diventare "famosa" ecco che potrete al volo comprare dei dyno aggiuntivi, dove il dyno è la singola unità di computazione come spiegato a questo indirizzo; se nessuno naviga la vostra applicazione, essa va automaticamente in standby, sospendendo il conteggio per il calcolo del costo finale: si pagano le risorse che vengono effettivamente utilizzate.

Tranquilli, per i nostri scopi non pagheremo nulla: obiettivo di questo post è installare (nel gergo informatico anglosassone si dice : to deploy) l'applicazione del post precedente su heroku.

Ecco i passi necessari
  1. Prerequisiti:  node.js e github gia installati (e spero che a questo punto lo siano :))
  2. Dopo aver installato github, usate la git shell per i prossimi comandi, in modo da non avere problemi di variabili d'ambiente. 
  3. Createvi un account su heroku
  4. Fate il download e installate il toolbelt heroku per la vostra piattaforma
  5. Aprite una shell e digitate heroku login inserendo le credenziali di accesso del vostro account
  6. Installate in locale un template di progetto creato da heroku su github per avere i file di configurazione necessari: digitate  git clone https://github.com/heroku/node-js-getting-started.git
  7. Posizionatevi nella cartella  di progetto node-js-getting-started e digitate heroku create per creare l'applicazione da installare su heroku: heroku creerà un nome random per la vostra applicazione e un repository git remoto dal nome heroku 
  8. Per installare effettivamente l'applicazione sulla piattaforma heroku,digitate git push heroku master
  9. Per vedere l'applicazione in esecuzione digitate heroku open:  si aprirà il vostro browser che punterà all'indirizzo scelto da heroku per la vostra applicazione
Partendo da questo scheletro di progetto, potete fare tutte le modifiche che volete e reinstallare il tutto.

Facciamo ad esempio queste modifiche:
  • Inseriamo una semplice pagina index.html nella cartella public
 <!DOCTYPE html>  
 <html lang="en">  
 <head>  
      <meta charset="UTF-8">  
      <title>Node js</title>  
 </head>  
 <body>  
      <p>Applicazione node.js su heroku ok!</p>  
 </body>  
 </html>  
  • Rinominiamo il file index.js in app.js e apportiamo le seguenti modifiche
 var express = require('express');  
 var app = express();  
   
 app.set('port', (process.env.PORT || 8000));  
   
 app.use(express.static(__dirname + '/public'));  
   
 app.listen(app.get('port'), function() {  
  console.log("Node app is running at localhost:" + app.get('port'));  
 });  

Poichè la porta su cui andrà in esecuzione è decisa da heroku, non possiamo mandare in esecuzione il server su una porta qualsiasi ma utilizzeremo la porta definita dalla piattaforma in process.env.PORT: se tale porta non è definita, perchè magari eseguiamo noi in locale l'applicazione, verrà utilizzata la porta 8000.
  • Sostituire nel file Procfile e in package.json il riferimento a index.js con app.js
Ora dobbiamo aggiornare il repository git remoto digitando, sempre dalla cartella root del progetto e usando la git shell
  1. git add --all
  2. git commit -m "apportate modifiche ... "
  3. git push heroku master
Riaprite il browser all'indirizzo dell'applicazione o direttamente o digitando heroku open.

La mia applicazione è installata a questo indirizzo: https://whispering-peak-1284.herokuapp.com/

Ovviamente è possibile, in un secondo momento, acquistare un dominio e associarlo all'applicazione.

Provate a ripetere i passi precedenti e ad installare su heroku una vostra applicazione: il primo che posterà nei commenti l'indirizzo della sua applicazione ... contribuirà semplicemente alla mia soddisfazione personale :)

In questo post le istruzioni su come usare github, nel caso non lo abbiate mai utilizzato.

E per qualsiasi dubbio, commentate :))

Alla prox.
Ivan

martedì, dicembre 16, 2014

Filtri per la pulizia in Express per node.js

Se state seguendo il mio blog, dovreste saperlo, ci tengo alla pulizia architetturale dei sistemi informativi.

Express eccelle da questo punto di vista, utilizzando il concetto di middleware: componenti software che intercettano la richiesta http per compiere una qualsiasi azione utile, attivabili e disattivabili in maniera semplice e trasparenti per la gestione finale della richiesta.


Osservando la figura, vediamo come la richiesta venga intercettata dal middleware di validazione, autenticazione, data parsing prima di essere gestita effetivamente dalla funzione di callback responsabile di creare la risposta finale; il middleware di validazione, ad esempio, controlla la presenza corretta di eventuali parametri, e se alcuni parametri non sono validi, puo' deviare il normale flusso di gestione della richiesta, altrimenti passa la palla al middleware successivo, senza ovviamente sapere qual è.

Esistono middleware standard inclusi nella distribuzione di express, middleware forniti da terze parti come moduli scaricabili con npm, possiamo anche definire i nostri middleware
 app.use(function(request, response, next) {  
   // eseguo la logica del middleware: es. valido i parametri e se tutto ok 
   // invoco la funzione next() che passa la richiesta al middleware successivo  
   // o alla funzione di callback finale definita in app.get(...)  
   next();  
 });  
Nel nostro programma app.js, tramite il metodo use() dell'oggetto app, attiviamo un particolare middleware, che non è altro che, guarda un po', una funzione di callback che accetta l'oggetto request, l'oggetto response e la funzione next per far proseguire il normale flusso di gestione della richiesta verso un particolare url; se per qualche motivo vogliamo interrompere tale flusso, abbiamo l'oggetto response per mandare una risposta immediata al client e non eseguiremo più la funzione next.

Come primo esempio, proviamo a definire un middleware di validazione parametro, che controlli la presenta del parametro dal nome totale che deve essere un intero: se la validazione fallisce ritorniamo una opportuna risposta deviando dal normale flusso di gestione della richiesta
 var express = require('express');  
   
 var app = express();  
   
 app.use(function(req,res,next) {  
      if (Number(req.query.totale))  
           next();  
      else  
           res.end('Usare un intero per il parametro totale!');  
 });  
   
 app.get('/', function(req,res) {  
      res.end('Tutto ok!');  
 });  
   
 app.listen('8000', function() {  
      console.log('Server Web in ascolto su 8000 ...');  
 });  
Provate ad effettuare una richiesta con browser del tipo http://localhost:8000 o http://localhost:8000/?totale=NonUnNumero e otterrete la risposta del middleware, altrimenti riceverete la risposta tutto ok.

Se vogliamo utilizzare diversi middleware, l'ordine con cui scriviamo i vari app.use(...) determina l'ordine di esecuzione.

Vediamo ora l'esempio di utilizzo di un middleware molto utile, presente nella distribuzione standard di express
 var express = require('express');  
   
 var app = express();   
   
 app.use(express.static('public'));  
   
 app.listen('8000', function() {  
      console.log('Server Web in ascolto su 8000 ...');  
 });  
express.static('public') ritorna un middleware, utilizzato dalla nostra app, che si preoccuperà di servire direttamente i file presenti nella cartella public che rappresenta effettivamente una cartella virtuale: se ad esempio qualcuno richiede http://localhost:8000/ verrà, di default, servita la pagina index.html presente nella cartella public; se qualcuno richiede http://localhost:8000/img/header.jpg verrà servita l'immagine presente in public/img/header.jpg.

La struttura dei file del nostro progetto è diventata così:


Tutte le risorse statiche (html, css, javascript da eseguire lato cliente, immagini) verranno servite direttamente dal middleware in questione.

Con queste semplici istruzioni, abbiamo implementato un server web statico perfettamente funzionante.

Con i prossimi post, vedremo come dinamicizzare le risposte.

Ho messo su github questo esempio di applicazione con middleware, quando copierete in locale il progetto, ricordatevi di eseguire npm install dalla cartella del progetto per scaricarvi il modulo express.

In questo post le istruzioni su come usare github, nel caso non lo abbiate mai usato.

E se avete dubbi sui filtri di pulizia, commentate :))

Alla prox.
Ivan

lunedì, dicembre 15, 2014

Prendiamo l'Express per le applicazioni web in node.js

Express è un modulo di node.js che permette di sviluppare un'applicazione web in maniera semplice e pulita dal punto di vista architetturale.

Conosciamo tutti come funziona un classico sito web statico: facciamo una richiesta http con il browser ad un determinato indirizzo, il server risponderà inviandoci il contenuto di un file html presente in una cartella mappata con il particolare indirizzo richiesto; il server restituisce sempre, per ogni richiesta allo stesso indirizzo, lo stesso documento html.

Nel caso in cui volessimo una pagina html per visualizzare i 10 prodotti più venduti della settimana, in un ipotetico sito di vendite online, il discorso si complicherebbe: dovremmo prevedere l'utilizzo di una tecnologia lato server che ci permetta di generare dinamicamente il codice html necessario, magari andando ad interrogare una base dati per avere la lista dei prodotti richiesti.

Riprendiamo l'esempio di questo post, immaginando di voler ritonare un documento html completo, per osservare come saremmo già in grado di generare un html diverso a secondo della richiesta
 var http = require('http'),   
    url = require('url'),  
    html = '';  
     
  var server = http.createServer(function (req, res) {   
    var urlRichiesta = url.parse(req.url, true);   
    res.writeHead(200, {'Content-Type': 'text/html'});   
      
    html = '<!DOCTYPE html>';  
    html += '<html lang="en">';  
    html += '     <head>';  
    html += '          <meta charset="UTF-8">';  
    html += '          <title>Esempio server node</title>';  
    html += '     </head>';  
    html += '     <body>';  
    html += '          <p>';  
    html += '             Hello World: ' + urlRichiesta.query.nome  
    html += '          </p>';  
    html += '     </body>';
    html += '</html>'; 
   
    res.write(html);   
    res.end();   
  });   
     
  server.listen(8080, function() {   
    console.log("Server Web in ascolto su 8080 .....");   
  });   
Notate che abbiamo semplicemente variato il content-type della risposta e costruito l'intera risposta html all'interno di una stringa che viene inviato allo stream di destinazione.

Dovrebbe risultare chiaro che questo approccio va bene solo per semplici esempi come questo: nel caso di pagine più complesse, è buona norma separare la logica di business dalla logica di presentation.

Express ci aiuta proprio in questo, fornendoci gli strumenti per disegnare la nostra applicazione in maniera pulita e facile da manutenere: provate a modificare la risposta html nell'esempio precedente!

Cominciamo dunque a sviluppare un'applicazione web con express e node.js.

Creiamo una cartella vuota che sarà la cartella padre del nostro progetto web e creaiamo, al suo inteno, un file dal nome package.json con il seguente contenuto
 {  
  "name": "PrimaAppExpress",  
  "version": "0.0.1",  
  "private": "true",  
  "main": "app.js",  
  "dependencies": {  
     "express": "*"  
   }  
 }  
Ogni applicazione per node.js puo' specificare un file di questo tipo, per descrivere le proprietà del progetto e le dipendenze nei confronti di librerie esterne.

In questo caso, la nostra applicazione PrimaAppExpress
  1. ha una versione 0.0.1
  2. è un modulo privato (non censibile nel repository centralizzato di npm)
  3. il file principale che viene mandato in esecuzione è app.js
  4. dipende da modulo esterno express, ultima versione per la presenza del carattere '*', altrimenti indicare la versione esatta
Ora posizionamoci con una shell dos nella directory del progetto e digitiamo il comando npm install che provvederà a scaricare dal repository di npm, le varie dipendenze elencate nel file package,json; in questo casò solo il modulo express, che sarà copiato in una sottocartella dal nome node_modules

Ora creaiamo il file app.js
 var express = require('express');  
   
 var app = express();  
   
 app.get('/', function(req,res) {  
      res.end('Tutto ok!');  
 });  
   
 app.listen('8000', function() {  
      console.log('Server Web in ascolto su 8000 ...');  
 });  
In questo primo esempio, minimale, creaiamo un oggetto app utilizzando la funzione express() importata con l'istruzione require.

Definiamo poi la risposta da ritornare quando arriva la richiesta all'indirizzo '/', rappresentato da http://localhost:8000/ (localhost solo perchè il server è sulla stessa macchina fisica del browser),in questo caso ritorniamo semplicemente la stringa 'Tutto ok', solo per testare che tutto funzioni.

Non abbiamo ancora separato logica di business da logica di presentazione, ci mancano ancora un po' di concetti che affronteremo un passo alla volta.

Ho messo su github questo primo esempio di applicazione, quando copierete in locale il progetto, ricordatevi di eseguire npm install dalla cartella del progetto per scaricarvi il modulo express.

In questo post le istruzioni su come usare github, nel caso non lo abbiate mai usato.

Alla prox.
Ivan




Stream e pipe (con una p ...)

Con questo post, vorrei approfondirei con voi i concetti di stream e pipe presenti nella piattaforma node.js

Uno stream non è altro che un flusso di dati dinamico: può essere una sorgente di dati, una destinazione di dati o entrambi.

Una pipe è un condotto che unisce una sorgente dati ad una destinazione dati, preoccupandosi di adattare le eventuali differenti velocità di elaborazione dati delle sorgenti e delle destinazioni.


Il concetto di Stream è un'astrazione: i dati veri e propri possono risiedere in un file così come provenire da un server, l'utilizzo degli stream e pipe è identico indipendentemente dal tipo di sorgente/destinzaione dati.

In pratica uno stream è un emettitore di eventi, che implementa particolati metodi: a seconda dei particolari metodi implementati si parla di Readables Stream, Writable Streams e Duplex

Readable Streams
Uno stream di lettura ci permette di leggere dati da una sorgente. La sorgente puo' essere qualsiasi cosa: un file sul file system, un buffer in memoria o ancora un altro stream.
Per leggere da uno stream basta mettersi in ascolto dell'evento data emesso dai stream di tipo readable.
 var fs = require('fs');  
 var streamLettura = fs.createReadStream('file.txt');  
 var data = '';  
 
 streamLettura.setEncoding('utf-8');
 
 streamLettura.on('data', function(chunk) {  
   data+=chunk;  
 });  
    
 streamLettura.on('end', function() {  
   console.log(data);  
 });  
In questo caso, la sorgente dati è costituito da un file.

Inizialmente lo stream è in uno stato statico ma non appena attacchiamo una funzione di callback all'evento data i dati cominciano a fluire fino a che terminano e lo stream emette l'evento end che possiamo intercettare con un'altra funzione di callback.

Se questo codice vi ricorda la soluzione a questo esercizio, siete sulla buona strada per capire la flessibilità di questo meccanismo: indipendentemente dal tipo di sorgente dati, la gestione dei dati viene sempre fatta allo stesso modo; l'oggetto response, in questo caso, è uno stream readable la cui sorgente dati è un socket di rete.

Di default, i dati letti da uno stream sono un oggetto di tipo Buffer, impostando il corretto encoding (ad es. utf-8), i dati vengono passati alla funzione di callback in formato stringa.

Writable Streams
Uno stream di scrittura ci permette si scrivere dati su una destinazione. Supponiamo di voler copiare un contenuto di un file in un altro file
 var fs = require('fs');  
 var readableStream = fs.createReadStream('file.txt');  
 var writableStream = fs.createWriteStream('file2.txt');  
    
 readableStream.setEncoding('utf8');  
    
 readableStream.on('data', function(chunk) {  
   writableStream.write(chunk);  
 });  
Semplice e intutitivo.

Il metodo write ritorna true o false a seconda dell'effettiva elaborazione dei dati in scrittura, se ritorna false, significa che dati aggiuntivi vengono memorizzati in memoria fino a che lo stream di scrittura non diventa di nuovo pronto: uno stream di scrittura emette l'evento drain per segnalare che è di nuovo pronto ad accettare dati senza bufferizzarli.

Se abbiamo dunque uno stream di lettura veloce ed uno stream di scrittura lento, la soluzione vista sopra potrebbe non essere efficiente perchè i dati emessi dalla sorgente verrebbero parcheggiati in un buffer in memoria, rallentando l'elaborazione dei dati; per rendere efficiente una elaborazione di questo tipo si utilizzano le pipe

Pipe
Vedetelo come un condotto che unisce sorgente e destinazione, che si preoccupa di far arrivare i dati a destinazione quando è effetivamente pronta a riceverli: anche in questo caso l'utilizzo è semplice e intuitivo
 var fs = require('fs');  
 var readableStream = fs.createReadStream('file1.txt');  
 var writableStream = fs.createWriteStream('file2.txt');  
    
 readableStream.pipe(writableStream);  
L'esempio copia i dati di file1.txt in file file2.txt, creandolo se non esiste.

Duplex
Sono stream che si comportano sia some sorgente che destinazione di dati: pensate ad esempio ad un socket di connessione TCP.
Immaginate un servizio in ascolto su una porta che gestisce un socket che è fullduplex: può ricevere dati e mandare dati sul socket. Sembra comlesso vero? Vediamo come implementare un server TCP di questo tipo con node.js
 var net = require('net');  
 var server = net.createServer(function(socket) {  
    console.log('server connesso');  
    socket.on('end', function() {  
       console.log('server disconnected');  
    });  
    socket.write('Benvenuto dal server eco ;))\r\n');  
    socket.on('data', function(data) {  
       console.log('ricevuti ' + data);
    });  
    socket.pipe(socket);  
 });  

 server.listen(8000, function() {  
    console.log('server in esecuzione su porta 8000');  
 });  
   

Per implementare un server TCP abbiamo bisogno del modulo net.

Creiamo un server con net.createServer() fornendo la funzione di callback che verrà eseguita ogni volta che un client si connette al nostro server.

Con il metodo listen mettiamo in ascolto il servizio sulla porta specificata come argomento.

La funzione di callback riceve un oggetto di tipo stream duplex su cui il server puo' leggere e/o scrivere i dati, secondo il protocollo che si vuole mettere in piedi.

In questo caso, per ogni connessione effettuata da un client, il server saluta e aspetta dati dal client che vengono immediatamente ritornati al client stesso tramite l'operazione di pipe effettuata da sorgente (socket) e destinazione (sempre socket!).

Per testare l'applicazione, eseguite il programma con node programma.js, il server si metterà in ascolto sulla porta 8000, per terminare il server digitare ctrl + c nella console.


Aprite un'altra console e digitate telnet localhost 8000, sulla console apparirà il saluto del server, se provate a digitare un qualsiasi carattere, vi verrà immediatamente rispedito indietro dal server che traccerà sulla sua console i dati elaborati, grazie alla gestione dell'evento data sul socket sorgente.

Nel prossimo post vedremo come implementare un server http, in pochi passi saremo in grado di implementare complesse applicazioni web con javascript ;)

Ho aggiornato su github il progetto, effettuate un git pull per aggiornare i sorgenti che avete in locale, come mostrato in questo post

Come al solito, per dubbi e/o domande: commentate!

Alla prox.
Ivan

mercoledì, dicembre 10, 2014

Esercitazioni su node.js: esercizio 10

Testo dell'esercizio
Ancora un esercizio sull'uso degli stream associati a richieste e risposte http.
Creare un server http che riceva, in streaming sulla richiesta, un file da salvare nella directory corrente di lavoro, rappresentata dalla variabile globale __dirname.
Creare un client http che effettui una richiesta POST al server, mandando in streaming sulla richiesta il contenuto di un file di testo.

Suggerimenti
Trovate una bozza di soluzione a questo indirizzo: si tratta di implementare due programmi, un client http ed un server http. 
Il client crea un oggetto richiesta, passando opportuni opzioni di configurazione (indirizzo server, porta) e manderà in streaming sulla richiesta un file di testo tramite una pipe, chiudendo la richiesta quando la pipe ha finito di travasare i dati dal file alla richiesta stessa.
Il server, dualmente, legge in streaming la richiesta e scriverà i dati ricevuti su uno stream di scrittura associato ad un file, sempre tramite una pipe: appena finito chiuderà la risposta.

Buon Lavoro!

Una possibile soluzione
Vediamo prima il codice del programma client
 var http = require('http'),   
     fs = require('fs');  
   
 var options = {  
    host: 'localhost',   
    port: 8000, path: '/',   
    method: 'POST'  
 };  
   
 var req = http.request(options, function(res) {  
    console.log(res.statusCode);  
 });  
   
 var readStream = fs.ReadStream(__dirname + '/in.txt');  
        
      readStream.pipe(req);  
   
      readStream.on('close', function () {  
            req.end();  
            console.log('Io ho terminato di mandare il file al server...');  
      });
      console.log('Resto in attesa di chiusura connessione da parte del server ...');
Il codice è molto pulito e intuitivo, a patto di ricordarsi dell'asincronicità di alcune funzioni.
Per fare una richiesta http ad un server, viene creato un oggetto options con i dettagli di conettività: serve a configurare una richiest http di tipo post al server in esecuzione su localhost e porta 8000.

Viene utilizzato il modulo http per fare la richiesta. Il metodo request ritona subito uno stream di scrittura: tutto cio' che mandiamo su questo stream arriverà al server: la funzione di callback passata al metodo request viene eseguita solo quando il server finisce di rispondere.

Vine creato uno stream di lettura per il file in.txt presente nella cartella di lavoro, rappresentata dalla variabile globale __dirname: tramite una pipe viene scritto il suo contenuto sullo stream di scrittura req.

Quando finisce il flusso di lettura, viene invocato il metodo end() dello stream per permettere al server di elaborare la richiesta.

Vediamo ora la parte server
 var http = require('http'),   
     fs = require('fs'),  
     writeStream;  
   
 var server = http.createServer(function (req, res) {  
    writeStream = fs.createWriteStream(__dirname +   
                         'out_' +   
                         new Date().getTime() +  
                         '.txt');  
    req.pipe(writeStream);  
    req.on('end', function() {  
       writeStream.end();  
       res.statusCode = 200;  
       res.end('OK');  
    });  
 });  
   
 server.listen(8000, function() {  
      console.log('server http in ascolto sulla porta 8000');  
 });  
In maniera duale, per ogni richiesta che arriva al server, viene creato uno stream di scrittura associato ad un file presente in __dirname e con nome out_msda1/1/1970.txt su cui viene mandata in pipe il flusso di lettura rappresentato dalla richiesta.

Quando il flusso della richiesta è terminato, viene chiuso il flusso di scrittura del file e terminata la richiesta, con un codice standard di 200 (così come definito dal protocollo http) che causerà la chiusura della connessione.

Per testare il programma, mandate prima in esecuzione il server e poi il client da due shell diverse, magari provate a mandare in esecuzione, sempre su shell diverse, più programmi client,

Notate come, nonostante la funzione di callback sul client venga eseguita abbastanza in fretta, il server ci mette un po' a chiudere la connessione e quindi a far terminare il client: credo che sia una anomalia che dovrebbe essere messa a posto al più presto.

Ho aggiornato su github il progetto, effettuate un git pull per aggiornare i sorgenti che avete in locale, come mostrato in questo post

Come al solito, per dubbi e/o domande: commentate!

Alla prox.
Ivan

martedì, dicembre 09, 2014

Ancora su modulo http in node.js

In questo post, traduco alcuni passaggi di un bell'articolo raggiungibile a questo indirizzo.

Scalabilità, big data, real time: queste le scommesse che node.js vuole vincere rispetto alla concorrenza grazie al suo modello di input/output non bloccante.

A patto di saper usare correttamente gli stream ...

Implementiamo un semplice server web che fornisca, ad ogni richiesta, il contenuto di un file statico presente sul server
 var http = require('http'),   
    fs = require('fs');  
    
 var server = http.createServer(function (req, res) {  
       fs.readFile(__dirname + '/data.txt', function (err, data) {  
       res.end(data);  
    });  
 });  
    
 server.listen(8000);  
La soluzione presentata funzione alla perfezione, collegatevi con un browser all'indirizzo localhost:8080, dopo aver mandato in esecuzione il programma, e riceverete il contenuto del file data.txt presente nella directory di lavoro, rappresentata dalla variabile globale __dirname.

Il programma legge il file in modalità asincrona e poi esegue la funzione di callback che ne scrive il contenuto sullo stream associato alla risposta: peccato che tutto il file venga prima caricato in memoria; se arrivano n richieste contemporanee  (con n speriamo grande :)), il nostro server si troverà seduto in men che non si dica.

Per rendere il nostro server scalabile, dobbiamo utilizzare un altro approccio, basato sul corretto utilizzo di stream e pipe
 var http = require('http'),   
     fs = require('fs');  
    
 var server = http.createServer(function (req, res) {  
        var stream = fs.createReadStream(__dirname + '/data.txt');  
        stream.pipe(res);  
     });  
 server.listen(8000);  
Creaiamo uno stream in lettura associato al file come sorgente dati e lo mandiamo sullo stream della risposta tramite la pipe.

Con questa soluzione, i dati verranno mandati al server, in blocchi, non appena disponibili, senza necessità di bufferizzazione sul server.

Con i prossimi post, vedremo come implementare un server web per creare la nostra prima applicazione web dinamica.

Ho aggiornato su github il progetto, effettuate un git pull per aggiornare i sorgenti che avete in locale, come mostrato in questo post

Come al solito, per dubbi e/o domande: commentate!

Alla prox.
Ivan

venerdì, dicembre 05, 2014

Primo server web in node.js ... troppo difficile

Uno dei motivi per cui ho deciso di investire del tempo nello studio di node.js è la sua semplicità nell'implementare componenti complessi.

Provate a creare un server http in java, o peggio ancora in c :)

Signore e signori ecco un semplice server web che, per ora, risponde ad ogni richiesta con il classico hello world
 var http = require('http');  
   
 var server = http.createServer(function (req, res) {  
  res.writeHead(200, {'Content-Type': 'text/plain'});  
  res.write('Hello World\n');  
  res.end();  
 });  
   
 server.listen(8080, function() {  
      console.log("Server Web in ascolto su 8080");  
 });  

Creiamo un oggetto server con il metodo createServer del modulo http, implementiamo la funzione di callback che verrà eseguita ogni volta che arriva una richiesta e lo mettiamo in ascolto sulla porta che vogliamo (a patto che sia libera), in questo caso la 8080.

Eseguite il programma con l'interprete node, non appena il server è pronto a ricevere richieste, stamperà a video il messaggio Server Web in ascolto su 8080 così come specificato nella funzione di callback passata come parametro al metodo listen.



Per testare il server, puntate il browser all'indirizzo http://localhost:8080 e gongolatevi per la vostra bravura: un server http in meno di un minuto :))

Per spegnere il server, digitate ctrl + c sulla console

La funzione di callback che gestisce le richieste riceve due parametri req e res (sono parametri formali, potreste chiamarli anche ciccio e paperino, ma req e res mi sembra più chiaro...) che sono rispettivamente uno stream di lettura e uno stream di scrittura.

Prima di proseguire, poichè ogni modifica al programma server implicherebbe il riavvio manuale del server (con ctrl + c e poi node programma.js), installiamo a livello globale il modulo nodemon con il comando npm install -g nodemon.

D'ora in avanti, eseguiremo il nostro programma digitando nodemon programma.js, in questo modo ogni modifica al file programma.js causa un riavvio automatico del programma stesso, il che vi farà risparmiare del tempo in fase di sviluppo.


Proviamo a passare un parametro nome alla richiesta, quindi le nostre richieste saranno del tipo http://localhost:8080/?nome=ivan, vediamo come si leggono i parametri dalla richiesta per rendere dinamica la risposta
 var http = require('http'),  
     url = require('url');  
   
 var server = http.createServer(function (req, res) {  
     var urlRichiesta = url.parse(req.url, true);  
     res.writeHead(200, {'Content-Type': 'text/plain'});  
     res.write('Hello World: ' + urlRichiesta.query.nome);  
     res.end();  
 });  
   
 server.listen(8080, function() {  
      console.log("Server Web in ascolto su 8080 .....");  
 });  

Importiamo il modulo url che semplifica il parsing dell'indirizzo della richiesta.

Dalla documentazione ufficiale dell'oggetto req, notiamo che la proprietà req.url rappresenta l'indirizzo della richiesta corrente: /?nome=ivan, utilizzando il modulo url con il suo metodo parse, è possibile ottenere un oggetto con le singole parti costituenti l'indirizzo della richiesta, la nostra variabile urlRichiesta sarà dunque costituita dal seguente oggetto
{ 
   href: '/?nome=ivan',  
   search: '?nome=ivan',  
   query: { nome: 'ivan' },  
   pathname: '/' 
}  
Per estrarre il valore del parametro nome basterà allora navigare l'oggetto urlRichiesta.query.nome, otterremo cosi un saluto dinamico :).

E se i parametri sono due? Ad esempio http://localhost:8080/?nome=ivan&cognome=saracino? 
Basterà ovviamente navigare l'oggetto urlRichiesta.query.cognome.

Ora avete la vostra prima applicazione web, servita da node.js, in esecuzione sulla vostra macchina sulla porta 8080: e se volessimo rendere accessibile la nostra stupenda applicazione in internet senza usare un servizio di hosting esterno e soprattutto, gratis?

Installate il modulo ngrok con il comando npm install -g ngrok, dopo aver messo in esecuzione il vostro server http sulla porta 8080, digitate su un'altra console il comando ngrok 8080, comparirà sulla console un output di questo tipo


Prendete nota dell'indirizzo di forwarding, nel mio caso è http://3d723a32.ngrok.com: ora la vostra applicazione, in esecuzione in locale sullo porta 8080, è accessibile dall'esterno a quell'indirizzo, provate ad inserire nel browser http://vostroindirizzo?nome=ivan&cognome=saracino e vedrete la vostra applicazione in esecuzione.

Ovviamente il server deve essere in esecuzione, per interrompere il tunneling digitate ctrl+c sulla console dove avete mandato in esecuzione ngrok: ricordate però che alla prossima esecuzione l'indirizzo cambia: è un ottimo modo per far vedere una vostra applicazione che state sviluppando a qualcuno fuori dalla vostra rete aziendale o domestica che sia.

Abbiamo appena iniziato l'affascinante argomento dello sviluppo di applicazioni web in node.js: vedremo come utilizzare ulteriori moduli di node per rendere lo sviluppo ancora più immediato.

Ho aggiornato su github il progetto, effettuate un git pull per aggiornare i sorgenti che avete in locale, come mostrato in questo post

Come al solito, per dubbi e/o domande: commentate!

Alla prox.
Ivan

giovedì, dicembre 04, 2014

Esercitazioni su node.js: esercizio 9

Testo dell'esercizio
Ancora più semplice.
Scrivere un programma che scriva sulla console ciò che digitate dalla tastiera.
Vincoli: utilizzare solo stream e pipe.

Suggerimento
process.stdin è un readable stream ...dunque? Per interrompere il programma ctrl + c ;)

Buon Lavoro!

Una possibile soluzione
 process.stdin.pipe(process.stdout);  

Mandiamo su standard output, tramite pipe, ciò che arriva da standard input.

Alla prox.
Ivan

Esercitazioni su node.js: esercizio 8

Testo dell'esercizio
Bene, prima di continuare, vi propongo alcuni esercizi sugli stream e pipe.
Scrivere un programma che scriva sulla console il contenuto di un file di testo passato come argomento sulla linea di comando.
Vincoli: utilizzare solo stream e pipe.

Buon Lavoro!

Suggerimento
process.stdout è un writable stream su cui collegare, tramite pipe, un readable stream associato al file e process.stdout è lo standard output associato all'esecuzione del programma...

Una possibile soluzione
 var fs = require('fs'),  
     file = process.argv[2];  
   
 fs.createReadStream(file).pipe(process.stdout);  

Semplice e conciso: creo uno stream di lettura sul file e lo pippo sullo standard output :))

Alla prox.
Ivan

Esercitazioni su node.js: esercizio 7

Testo dell'esercizio
Giusto per ribadire il concetto di stream che genera eventi asincroni da gestire, scrivere un programma, come il precedente,  per effettuare una richiesta HTTP di tipo GET all'indirizzo http://cspnet.it, questa volta però, visualizzare tutti i dati della risposta solo quando la richiesta è terminata.
Come ulteriore vincolo, l'unica variabile globale, (a livello di intero programma, esterna a qualsiasi funzione) deve essere solo la variabile var http = require('http');

Suggerimenti
Ricordate che l'evento data viene generato piu' volte, al termine della visualizzazione della risposta (che sarà ovviamente la pagina html home del sito di csp), provate infatti a stampare il numero di volte che è stato emesso l'evento data.
Per il vincolo richiesto, non scordate le IIF

Buon lavoro!

Una possibile soluzione
 var http = require('http'),  
   i = 0;  
   
 (function() {  
      var totalData = "",  
          numeroChunk = 0;  
   
          http.get('http://cspnet.it', function (response) {  
             response.setEncoding('utf8');  
             response.on('data', function(data) {  
                totalData += data;  
                numeroChunk++;  
             });  
            response.on('error', console.error);  
            response.on('end', function() {  
                 console.log("FINITO: " +   
                        totalData +   
                       " NUMERO CHUNK " +   
                            numeroChunk);  
            });  
          });  
 })();  
Il prossimo post verterà sul potente concetto di stream in node.js.
L'oggetto response vedetelo come un flusso di dati che viene alimentato dal server e letto dal client quando i dati sono disponibili.

Per motivi di efficienza, i dati non sono mandati in un unico blocco, l'evento 'data' dello stream response viene emesso più volte ed ogni volta viene eseguita la funzione di callback che accoda i dati ricevuti nella variabile totalData e incrementa il contatore numeroChunk.

Quando non ci sono più dati, lo stream emette l'evento end, che ci permette di definire una funzione di callback che stampa a video i dati finali.

Ho aggiornato su github il progetto, effettuate un git pull per aggiornare i sorgenti che avete in locale, come mostrato in questo post

Come al solito, per dubbi e/o domande: commentate!

Alla prox.
Ivan


martedì, dicembre 02, 2014

Esercitazioni su node.js: esercizio 6

Ora i duri cominciano a giocare.

Testo dell'esercizio
Scrivere un programma per effettuare una richiesta HTTP di tipo GET ad un URL passato al programma come primo parametro dalla linea di comando.
Scrivere sulla console il contenuto testuale della rispota HTTP.

Suggerimenti
Per questo esercizio utilizzerete il modulo http, in particolare il metodo get(), presente nella distribuzione di base: la documentazione la potete trovare a questo link

La funzione di callback è di questo tipo function callback(response) ... dove response è un oggetto che rappresenta uno Stream in node.js: prossimamente approfondiremo il concetto di Stream, per ora ci basti sapere che uno Stream è un flusso di dati che puo' emettere particolari eventi gestibili da nostre funzioni di callback.
Gli eventi piu' importanti da gestire sono "data" , "error" e "end".
Ad esempio response.on("data", function(data) {}) ci permette di processare parzialmente i dati della risposta, non appena disponibili, in maniera asincrona: l'oggetto response puo' emettere più eventi "data" a seconda della dimensione della risposta effettiva del server.

L'oggetto response ha anche un metodo setEncoding(...) che ci permette di trattare il parametro data, ricevuto dalla funzione di callback degli eventi emessi dall'oggetto responsedirettamente come una stringa nella codifica scelta (usate 'utf-8'). 

Buon Lavoro!

Una possibile soluzione
Più semplice del previsto, grazie ai moduli a disposizione della piattaforma node.js
 var http = require('http');  
   
 http.get(process.argv[2], function (response) {  
  response.setEncoding('utf8');  
  response.on('data', console.log);  
  response.on('error', console.log);
  response.on('end', function() {console.log("FINITO ... RICHIESTA CONCLUSA");});
 });  
Invochiamo il metodo get del modulo http passando come parametro la funzione di callback che gestisce la risposta.

La risposta è un oggetto che rappresenta uno stream di dati che emette eventi che hanno un nome: data,error, end.

L'evento data viene emesso ogni volta che c'è un blocco di dati disponibile per l'elaborazione, (ricordate che ora stiamo programmando in maniera asincrona).

L'evento error viene emesso ogni qual volta c'è un errore nella comunicazione con il server.
L'evento end viene emesso quando la richiesta è conclusa.

Notate che il secondo parametro del metodo on, nel caso di evento data ed error, deve essere una funzione che accetterà come parametri i dati effettivi ottenuti dal server: possiamo direttamente passare la funzione console.log che si occuperà di stampare a video i dati o i messaggi di errore.

Ho aggiornato su github il progetto, effettuate un git pull per aggiornare i sorgenti che avete in locale, come mostrato in questo post

Come al solito, per dubbi e/o domande: commentate!

Alla prox.
Ivan



Esercitazioni su node.js: esercizio 5

Testo dell'esercizio
Riprendete l'esercizio precedente e cercate di modularizzarlo con la tecnica vista nel post precedente.

Suggerimenti
Il lavoro principale deve essere fatta dal modulo esterno al programma.
Non dovete stampare la lista di file dal modulo, pensate ad una funzione di callback definita nel programma principale che verrà passata come argomento ad un metodo del modulo esterno.
Usate la forma ideomatica callback(err, data) per gestire nel programma principale eventuali errori generati nel modulo.

Buon Lavoro!

Una possibile soluzione
Il programma principale usa il modulo fileUtils, che esporta un oggetto che espone il metodo filtra per eseguire la lettura della directory e scartare i file che non hanno l'estensione voluta.
Il programma principale fornisce al metodo filtra la funzione di callback che verrà invocata nel modulo.
Si osservi come vengono gestiti gli errori: il modulo, che fa il lavoro sporco, passa l'eventuale errore come primo parametro della funzione di callback definita nel programma principale.
 var fileUtils = require('./fileUtils'),  
     dir = process.argv[2],  
     filtro = process.argv[3];  
     fileUtils.filtra(dir, filtro, function (err, list) {  
           if (err)  
             return console.error('Errore: ', err);  
           list.forEach(function (file) {  
             console.log(file);  
           });  
      });  

Il modulo fileUtils, usa una iife (se non vi ricordate cosa è andate rileggete questo post :)) per creare un oggetto che esponen il metodo filtra.
 var fs = require('fs');  
 var path = require('path');  
   
 module.exports = (function () {  
      var filtra = function(dir, filtro, callback) {  
           fs.readdir(dir, function (err, list) {  
              if (err)  
                 return callback(err);  
              list = list.filter(function (file) {  
                 return path.extname(file) === '.' + filtro;  
              });  
              callback(null, list);  
           });  
      };  
   
      return {  
           filtra : filtra  
      };  
 })();  
Per filtrare i file dalla lista completa, si puo' utilizzare il metodo filter presente in Array.prototype, 
questo metodo accetta come parametro una funzione che ritorna true se si vuole tenere nell'array il file corrente, false se lo si vuole togliere.

Ho aggiornato su github il progetto, effettuate un git pull per aggiornare i sorgenti che avete in locale, come mostrato in questo post

Come al solito, per dubbi e/o domande: commentate!

Alla prox
Ivan

Impariamo a definire i nostri ravioli .. ehm ... moduli in node.js

Node.js è stato disegnato all'insegno della modularità.
Abbiamo già notato come esista una pletora di moduli utilizzabili da un programma node.js:
basta utilizzare l'istruzione require('nomemodulo') per utilizzare le funzionalità esportate dal modulo.

La piattaforma provvede una serie di moduli inclusi nella distribuzione di base, abbiamo visto ad esempio l'utilizzo del modulo fs o del modulo path

In questo post impariamo a definire i nostri moduli da utilizzare nei nostri programmi, per cercare di programmare utilizzando ravioli e non spaghetti

Una volta definito un modulo, è anche possibile pubblicarlo sul repository ufficiale dei moduli gestiti da npm.

Date un'occhiata alla lista ufficiale di moduli per node disponibili attualmente.

Se vi piace un modulo, basterà eseguire in una shell il comando npm  <nomemodulo> nella cartella radice del progetto che state sviluppando, per poterlo utilizzare con una istruzione di require('nomemodulo') dal vostro codice.

Ad esempio, supponete di aver bisogno della funzionalità di trasformazione dei vostri oggetti json in formato xml: basterà utilizzare il modulo di terze parti che si chiama xml registrato sul repository ufficiale.

Vi posizionate nella cartella del vostro progetto e digitate npm install xml: verrà creata una directory node_modules, all'interno del vostro progettoche contiene i sorgenti del modulo, se un modulo dipende da altri moduli, questi verranno a loro volta scaricati in locale, tutto in maniera semplice e trasparente.

Ogni modulo ufficiale ha una pagina di documentazione come questa in cui ci sono le informazioni su come utilizzarlo.

In questo caso basta scrivere del codice di questo tipo
 var xml = require('xml');  
   
 var xmlString = xml({libri :   
      [  
           {  
                libro: [  
                     {titolo : 'Il signore degli anelli'},   
                     {autore : 'Tolkien'}  
                ],  
           },  
           {  
                libro: [  
                     {titolo: 'Guerra e pace'},  
                     {autore: 'Tolstoj'}  
                ]  
           }  
      ]  
 }, true);  
   
 console.log(xmlString);  
Per ottenere sulla console
 <libri>  
   <libro>  
     <titolo>Il signore degli anelli</titolo>  
     <autore>Tolkien</autore>  
   </libro>  
   <libro>  
     <titolo>Guerra e pace</titolo>  
     <autore>Tolstoj</autore>  
   </libro>  
 </libri>  
I singoli moduli devono essere definiti in un file, il cui nome sarà il nome del modulo, all'interno del file si deve utilizzare una istruzione del tipo module.exports = {}: assegniamo alla proprietà export dell'oggetto globale module un qualsiasi oggetto che definiremo noi, magari una iife per incapsulare dettagli implementativi mediante il meccanismo delle closure.

Facciamo un esempio.
Immaginiamo di implementare un modulo biblioteca che ci permetta di salvare in memoria dei libri e di ottenere la lista attuale dei libri.

Il programma principale utilizza il modulo biblioteca
 var biblioteca = require('./biblioteca');  
   
 biblioteca.inserisciLibro({  
      titolo : "Il signore degli anelli",  
      autore : "Tolkien"  
 });  
   
 biblioteca.inserisciLibro({  
      titolo : "Guerra e pace",  
      autore : "Tolstoj"  
 });  
   
 biblioteca.elaboraLibri(function(error, libri) {  
      if (error)  
           console.log(error.messaggio);  
      else {  
           libri.forEach(function(libro) {  
                console.log("Titolo: " +   
                          libro.titolo +   
                          "- Autore: " +   
                          libro.autore);  
           });  
      }  
 });  
Il modulo biblioteca dovrà essere implementato in un file dal nome biblioteca.js presente nella stessa cartella del file programma.js che rappresenta il programma principale.
 module.exports = (function() {  
      var libri = [],  
          inserisciLibro = function(libro) {  
             libri.push(libro);  
          },  
          elaboraLibri = function(callback) {  
             if (libri.length === 0)  
                callback({messaggio: 'biblioteca vuota'}, null);  
             else  
                callback(null, libri);       
          };  
          
          return {  
             inserisciLibro : inserisciLibro,  
             elaboraLibri : elaboraLibri  
          };  
 })();  
Notate la prima istruzione che assegna a module.exports il risultato di una funzione che viene eseguita immediatamente, tale risultato è un oggetto che espone solo i metodi inserisciLibro ed elaboraLibri.

Notate anche l'utilizzo di una funzione di callback definita in programma.js e passata come argomento a eleboraLibri, tale funzione di callback verrà eseguita solo se non ci sono condizioni di errore (in questo caso rappresentate da una biblioteca vuota).

Ho aggiornato su github il progetto, effettuate un git pull per aggiornare i sorgenti che avete in locale, come mostrato questo post.
Ricordatevi, per il progetto esempio-xml, di posizionarvi nella cartella radice ed eseguire npm install xml per includere il modulo xml nel progetto.

Se avete dubbi e/o domande, commentate!

Alla prox.
Ivan


Esercitazioni su node.js: esercizio 4

Testo dell'esercizio
Creare un programma che stampi a video il contenuto di una cartella filtrato per estensione del file.
Il programma  accetterà da linea di comando, il percorso della cartella come primo argomento e l'estensione dei file da filtrare come secondo parametro.
Ad esempio, invocando node program.js c:/test txt, il programma visualizzerà la lista dei file della cartella c:/test con estensione .txt.

Suggerimenti
Consultare la documentazione del metodo fs.readdir(...).
Potrebbe essere anche utile consultare il modulo path, da includere nel programma.

Buon lavoro!

Una possibile soluzione
 var fs = require('fs'),  
     path = require('path');  
   
     fs.readdir(process.argv[2], function (err, listaFiles) {  
        listaFiles.forEach(function (file) {  
           if (path.extname(file) === '.' + process.argv[3])  
              console.log(file);  
        });  
     });  
   

La funzione di callback riceve come parametro un array di files.
Ogni array eredita il metodo forEach() da Array.prototype, che cicla l'array invocando un'altra funzione di callback che riceve come parametro l'elemento corrente dell'array.
Utillizando il modulo path, possiamo usare il metodo extname() che ritorna l'estenzione del nome file compreso il punto.

Ho aggiornato su github il progetto, effettuate un git pull per aggiornare i sorgenti che avete in locale, come mostrato in questo post

Alla prox.
Ivan

lunedì, dicembre 01, 2014

Esercitazioni su node.js: esercizio 3

Testo dell'esercizio
Scrivere un programma che utilizzi una singola operazione asincrona per leggere il contenuto di un file e stampare a video il numero di linee del file letto.
Il percorso completo del file verrà passato come primo argomento sulla linea di comando.

Suggerimenti
E' la versione asincrona dell'esercizio precedente. Dare un'occhiata a fs.readFile(...).
Riflettere su differenza programmazione sincrona vs asincrona.

Buon lavoro.

Una possibile soluzione

 var fs = require('fs'),  
     file = process.argv[2],  
     numeroLinee;  
   
     fs.readFile(file, function (err, contenutoFile) {  
        if (!err) {  
            numeroLinee = contenutoFile.toString().split('\n').length;  
            console.log(numeroLinee);  
        } else {  
            console.log("Problemi in lettura " + file);  
        }  
 });  

La lettura del file è asincrona, la funzione readFile non è bloccante.
Anche in questo caso si puo' specificare la codifica del file da leggere ed omettere l'invocazione del toString()

 var fs = require('fs'),  
    file = process.argv[2],  
    numeroLinee;  
   
    fs.readFile(file,'utf-8',function (err, contenutoFile) {  
        if (!err) {  
            numeroLinee = contenutoFile.split('\n').length;  
            console.log(numeroLinee);  
         } else {  
            console.log("Problemi in lettura " + file);  
         }  
 });  

Ho aggiornato su github il progetto, effettuate un git pull per aggiornare i sorgenti che avete in locale, come mostrato in questo post

Alla prox.
Ivan

Esercitazioni su node.js: esercizio 2

Testo dell'esercizio
Scrivere un programma che utilizzi una singola operazione sincrona per leggere il contenuto di un file e stampare a video il numero di linee del file letto.
Il percorso completo del file verrà passato come primo argomento sulla linea di comando.

Suggerimenti
Per lavorare con il filesystem avremo bisogno del modulo fs di node.js, che includiamo nel programma con l'istruzione

 var mioFs = require('fs');  

A questo punto date un'occhiata alla guida ufficiale del modulo fs, in particolare alla funzione mioFs.readSync(...) 

La funzione è sincrona e ritorna un Buffer, che è il modo standard che ha node.js di rappresentare un insieme di dati, in qualsiasi formato: ascii, utf-8, binario.

Se i dati sono in un formato testuale, basterà invocare il metodo toString(...) per ottenere la stringa corrispondente.

Metodo veloce per contare le linee in una stringa

 var stringa = "Quante linee\nci sono in\nquesta stringa?";  
 var linee = stringa.split('\n');  
 console.log(linee.length);  

Una possibile soluzione
 var fs = require('fs'),  
    contenutoFile = fs.readFileSync(process.argv[2]),  
    numeroLinee = contenutoFile.toString().split('\n').length;  
   
 console.log(numeroLinee);  

In questo caso, readFileSync è una funzione sincrona, ritorna a sinistra un Buffer di dati, che deve essere convertito in stringa con il metodo toString().

Alternativamente si puo' specificare la codifica particolare con cui leggere il file ed omettere la trasformazione con il toString()
 var fs = require('fs'),  
    contenutoFile = fs.readFileSync(process.argv[2], 'utf-8'),  
    numeroLinee = contenutoFile.split('\n').length;  
   
 console.log(numeroLinee);  

Ho aggiornato su github il progetto, effettuate un git pull per aggiornare i sorgenti che avete in locale, come mostrato in questo post

Alla prox.
Ivan