Visualizzazione post con etichetta express. Mostra tutti i post
Visualizzazione post con etichetta express. Mostra tutti i post

giovedì, gennaio 22, 2015

Biblioteca per node.js: conferma registrazione via posta elettronica

Come anticipato nel post precedente, vogliamo aggiungere alla nostra applicazione la funzionalità di conferma registrazione.

Il caso d'uso della funcionalità da aggiungere è il seguente
  • L'utente inserisce i dati per la registrazione
  • Il sistema memorizzerà le informazioni dell'utente e invierà un messaggio di posta elettronica per confermare la registrazione appena effettuata
  • Solo dopo aver confermato la registrazione, l'utente potrà effettuare l'accesso.
Ecco come vengono modificati i vari moduli.

Nel file delle rotte routes/utenti.js
 router.route('/conferma/:username')  
   .get(function(request,response){  
       serviziUtenze.confermaRegistrazione(request.params.username);  
       response.render('login', {registrazioneOk: 'Ora puoi effettuare il login'});  
    });  
aggiungiamo una rotta per gestire la conferma della registrazione: nei serviziUtente implementeremo un metodo ad hoc.

Nel file dei serviziUtenze modifichiamo i metodi di login, registrazione e aggiungiamo il nuovo metodo confermaRegistrazione
 var login = function(username,password) {  
           var utentiLoggati = utentiRegistrati.filter(function(utente) {  
                return utente.username === username &&   
                    utente.password === password &&  
                    utente.confermato === true;  
           });  
           return utentiLoggati[0];  
      };  
Il metodo login aggiunge il controllo sul nuovo campo booleano confermato, che viene inizializzato a false in fase di registrazione e posto a true con la funzionalità di conferma.
 var registra = function(utente) {  
      var href = 'http://whispering-peak-1284.herokuapp.com/utente/conferma/' + 
           utente.username;  
           utentiRegistrati.push(utente);  
           postino.sendMail({  
             from: 'passionejavascript@gmail.com',  
             to: utente.email,  
             subject: 'conferma registrazione',  
             html: 'Confermi la registrazione alla biblioteca con un click: ' +  
                   '<a href="' + href +   
                   '">Confermo!</a>'  
           }, function(err, info) {  
                  if (err)  
                    console.log(err);  
                  if (info)  
                    console.log(info);   
                });  
           console.log(utentiRegistrati);  
      };  
Nel metodo registra, viene inviato un messaggio di posta elettronica all'utente che si sta registrando, costruendo, nel corpp del messaggio in formato html, un opportuno indirizzo per attivare la funzionalità di conferma.
 var confermaRegistrazione = function(username) {  
      var utenteDaConfermare = utentiRegistrati.filter(function(utente) {  
           return utente.username === username;  
      });  
   
      if (utenteDaConfermare)  
           utenteDaConfermare[0].confermato = true;  
      };  
   
      return {  
           login : login,  
           registra : registra,  
           confermaRegistrazione: confermaRegistrazione  
      };
      ...
Nella conferma, cerchiamo l'utente nella collezione degli utenti registrati e modifichiamo a true la proprietà confermato.

Provate a testare la funzionalità all'indirizzo https://whispering-peak-1284.herokuapp.com/: registratevi con un indirizzo di posta valido, controllate la posta, attivate la conferma dal messaggio di posta e poi effettuate l'accesso.

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 la dipendenza aggiuntiva nodemailer: se volete eseguire il programma in locale digitate node app.js e puntate il browser all'indirizzo http://localhost:5000.

Alla prox
Ivan

mercoledì, gennaio 21, 2015

Invio posta elettronica con node.js

In questo post vedremo come inviare un messaggio di posta elettronica con node.js.

L'idea è di aggiungere alla nostra applicazione, una funzionalità di conferma iscrizione: quando un utente nuovo si registra, il sistema invierà un messaggio di posta elettronica con un link per confermare la registrazione.

Prima di modificare l'applicazione web, diamo un'occhiata al codice per inviare un messaggio di posta elettronica.

Per poterlo fare, dobbiamo appoggiarci ad un fornitore di servizio di posta elettronica, non possiamo che appoggiarci a gmail: sarà dunque necessario aprire un account di posta elettronica con gmail per inviare la posta elettonica tramite le credenziali dell'utenza creata.

Se non avete un account di gmail, potete crearlo al volo qui, dovete poi solo ricordarvi l'indirizzo di posta scelto e la password.

Ci avvaleremo del modulo nodemailer , che ci semplificherà notevolmente il lavoro da svolgere.

Ho preparato un frammento di programma per  mostrarvi nodemailer all'opera
 var servizioPosta = require('nodemailer');  
   
 var postino = servizioPosta.createTransport({  
   service: 'gmail',  
   auth: {  
     user: 'VOSTRO_NOME_UTENTE@gmail.com',  
     pass: 'VOSTRA_PASSWORD'   
   }  
 });  
   
 postino.sendMail({  
   from: 'sender@address',  
   to: 'ivan.saracino@cspnet.it',  
   subject: 'hello',  
   text: 'hello world from node.js!'  
 }, function(err, info) {  
   if (err)  
     console.log(err);  
   if (info)  
     console.log(info);   
 });  
Importiamo il modulo nodemailer che espone il metodo createTransport a cui devono essere passati dei parametri di configurazione, tra cui il nome utente e la password del vostro account di posta.

A quel punto il postino può consegnare la mail con il metodo asincrono sendMail con gli ovvi parametri.

Il parametro from verrà impostato dal gmail all'indirizzo con cui usate il servizio di invio mail, non vi venga in mente di fare degli scherzi pensando di forzare un altro indirizzo diverso dal vero mittente ;)

Spulciate la documentazione ufficiale di nodemailer nel caso abbiate bisogno di funzionalità più avanzate come l'invio di allegati o corpo del messaggio in formato html.

Per poter usare il servizio di gmail da un programma, con il vostro account, dovete configurare gmail abilitando le opzioni meno sicure a questo indirizzo.

Bene, come abbiamo visto, il codice è abbastanza banale.

Come usarlo nella nostra applicazione rispettandone l'architettura?

Provate a rifletterci su .. fino al prossimo post :)

Alla prox.
Ivan






martedì, gennaio 20, 2015

Biblioteca: funzionalità di ricerca

Anche questa funzionalità s'implementa abbastanza in fretta all'interno dell'architettura proposta.

Si aggiunge una rotta opportuna nel file routes/libri.js
 router.route('/cerca')  
      .get(function(request, response) {  
           response.render('cercalibro');  
      })  
      .post(validatoreRicercaLibro, function(request, response) {  
           if (!request.form.isValid) {  
                messages = request.form.errors;  
                response.render('cercalibro', {messages: messages});  
           }  
           else {  
                var libri = serviziLibri.cercaLibri(request.body.chiave);  
                response.render('listalibri', {libri : libri});  
           }  
      });  
Il che significa che la richiesta GET a /libri/cerca fa visualizzare la pagina cercalibro,jade contentente il form di ricerca, mentre una richiesta POST allo stesso indirizzo scatena l'invocazione del servizio di ricerca e fa visualizzare la pagina listalibri,jade valorizzando opportunamente l'oggetto {libri: libri} utilizzato nel template jade.

La pagina cercalibro.jade contiene il form di ricerca
 extends ./layout.jade  
   
 block titolo  
  title Cerca Libro  
   
 block testata  
  h1.text-info Cerca Libro  
   
 block contenuti-principali  
  form(method='post', action='cerca')  
   div.form-group  
    label(for='chiave') Chiave ricerca  
    input(type='text', class='form-control' name='chiave', placeholder='chiave')  
   button(type='submit' class='btn btn-default') Cerca  
Il modulo servizi/serviziLibri esporrà il nuovo metodo per restituire i libri contenenti la chiave di ricerca nel proprio titolo
     ...  
   
 var cercaLibri = function(chiave) {  
    return libri.filter(function(libro) {  
       return (libro.titolo.toLowerCase().search(chiave.toLowerCase()) != -1);  
    });  
 };  
 return {  
    listaLibri : listaLibri,  
    elimina: elimina,  
    salvaLibro: salvaLibro,  
    cercaLibri: cercaLibri  
 };  
La funzionalità è implementata usando il metodo filter degli array e il metodi toLowerCase e search delle stringhe.

All'indirizzo https://whispering-peak-1284.herokuapp.com/ è presente l'applicativo aggiornato.

Con questo post, abbiamo completato l'implementazione di tutte le funzionalità: non resta che concentrarci sullo strato di persistenza su base dati.

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 (se non lo avete mai fatto) 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

lunedì, gennaio 19, 2015

Biblioteca per node.js: una soluzione per le funzionalità di amministrazione

Vediamo come sono state implementate le funzionalità di eliminazione ed inserimento di un nuovo libro.

Nella generazione della lista dei libri, solo per un utente amministratore, viene visualizzata una icona per l'eliminazione del libro: la vista listilibri.jade contiene il seguente codice
 block contenuti-principali  
      if (libri.length > 0)  
           table(class='table table-striped table-bordered')  
                thead  
                tr  
                     th Codice  
                     th Titolo  
                     th Autore  
                     if (session.utente.profilo === 'admin')  
                          th Elimina  
                each libro in libri  
                     tr  
                          td #{libro.codiceisbn}  
                          td #{libro.titolo}  
                          td #{libro.autore}  
                          if (session.utente.profilo === 'admin')  
                               td  
                                    a(href='/libri/elimina/#{libro.codiceisbn}')  
                                         img(src='/imgs/delete.png')  
      else  
           div.conmarginesuperiore  
                ul  
                     li.text-info Non ci sono libri in Biblioteca  
Notate l'espressione condizionale per l'aggiunta di una ulteriore intestazione e colonna all'interno della tabella.

Il link per l'eliminazione racchiude l'informazione del codice isbn del libro da eliminare, nel caso del signore degli anelli, ad esempio, verrà generato questo link /libri/elimina/DRF00223Z: l'ultima parte dell'indirizzo rappresenta un parametro di richiesta che potremo estrarre facilmente nel codice di gestione della richiesta.

Al file libri.js abbiamo aggiunto il codice seguente
 router.route('/elimina/:codiceisbn')  
      .get(function(request,response){  
           serviziLibri.elimina(request.params.codiceisbn);  
           response.redirect('/libri');  
      }); 
l'ultima parte dell'indirizzo della richiesta, viene automaticamente estratto e memorizzato come un parametro in request.params.codiceisbn, dopo aver effettuato l'eliminazione viene eseguita una nuova richiesta all'indirizzo /libri che riscatenerà la creazione della lista dei libri aggiornata.

Abbiamo aggiunto la funzionalità di eliminazione all'interno del file dei servizi per i libri
 ....  
 var elimina = function(codiceisbn) {  
           libri = libri.filter(function(libro) {  
                return libro.codiceisbn != codiceisbn;  
           });  
      };  
   
 ....  
 return {  
           listaLibri : listaLibri,  
           elimina: elimina,  
           salvaLibro: salvaLibro  
      };  
 ...  
La funzionalità di creazione nuovo libro è identica a quella di registrazione.
Partiamo del form definito in nuovolibro.jade
 block testata  
      h1.text-info Nuovo Libro  
   
 block contenuti-principali  
  form(method='post', action='nuovo')  
   div.form-group  
     label(for='codiceisbn') Codice Isbn  
     input(type='text', class='form-control' name='codiceisbn', placeholder='Isbn')  
   div.form-group  
     label(for='titolo') Titolo  
     input(type='text', class='form-control' name='titolo', placeholder='Titolo')  
   div.form-group  
     label(for='autore') Autore  
     input(type='text', class='form-control' name='autore', placeholder='Autore')  
   button(type='submit' class='btn btn-default') Salva!  
Al submit del form viene effettuata la richiesta di tipo POST all'indirizzo /libri/nuovo, richiesta gestita in questo modo
 ...  
 var validatoreNuovoLibro = form(  
           field('codiceisbn').trim().required('', 'Inserire il codice isbn!'),  
           field('titolo').trim().required('','Inserire Il titolo!'),  
           field('autore').trim().required('','Inserire autore!')  
      );   
 ...  
 router.route('/nuovo')  
      .get(function(request,response) {  
           response.render('nuovolibro');  
      })  
      .post(validatoreNuovoLibro,function(request,response) {  
           var messages;  
           var libro = {};  
           if (!request.form.isValid) {  
                messages = request.form.errors;  
           }  
           else {  
                libro.codiceisbn = request.body.codiceisbn;  
                libro.titolo = request.body.titolo;  
                libro.autore = request.body.autore;  
                serviziLibri.salvaLibro(libro);  
                messages = ['Libro memorizzato con successo'];  
           }  
           response.render('nuovolibro', {messages: messages});  
      });  
I parametri sono memorizzati nel corpo della richiesta http in request.body e sono validati da validatoreNuovoLibro

Abbiamo aggiunto una nuova funzionalità al file serviziLibri.js
 ...  
 var salvaLibro = function(libro) {  
           libri.push(libro);  
      };  
   
      return {  
           listaLibri : listaLibri,  
           elimina: elimina,  
           salvaLibro: salvaLibro  
      };  
 ...  

Risultato finale: https://whispering-peak-1284.herokuapp.com/

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 (se non lo avete mai fatto) 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ì, gennaio 16, 2015

Biblioteca per node.js: le funzionalità di amministrazione

In questo post implementeremo le funzionalità di eliminazione e inserimento di un libro nella nostra biblioteca.

Se andate al solito indirizzo https://whispering-peak-1284.herokuapp.com/ ed entrate con l'utente admin ivan con password saracino, troverete le funzionalità già implementate.

Quando visualizzerete la lista dei libri, noterete una immagine cliccabile di un cestino a fianco di ogni libro che implementa l'operazione di eliminazione del libro.

Provate a testare tali funzionalità e ad implementarle seguendo l'architettura proposta.

Nel prossimo post troverete la soluzione e l'intera implementazione :)

Buon Lavoro.
Alla prox.
Ivan



mercoledì, gennaio 14, 2015

Biblioteca: visualizziamo la lista dei libri

Ora che l'architettura della nostra applicazione è ben delineata, risulta molto semplice aggiungere le funzionalità rimanenti decise in fase di analisi

Con l'approccio modulare visto fino ad ora, basta aggiungere due ulteriori moduli per
  • la gestione delle rotte (richieste) relative alle gestione dei libri (lista, ricerca,rimozione ...) in routes/libri.js
  • implementazione logica di business relativa alla gestione dei libri in servizi/serviziLibri.js
Il nostro diagramma degli oggetti si modifica in questo modo


Il menu dinamico in funzione del profilo dell'utente che ha effettuato o meno l'accesso, lo modifichiamo in questo modo
 if (!session.utente)  
      ul(class='nav nav-pils')  
           li(role='presentation')  
                a(href='/utente/login') Login  
           li(role='presentation')  
                a(href='/utente/registrazione') Registrati!  
 else  
      ul(class='nav nav-pils')  
           li(role='presentation')  
                a(href='/utente/logout') Logout  
                li(role='presentation')  
                     a(href='/libri') Lista Libri  
                li(role='presentation')  
                     a(href='/libri/cerca') Cerca Libro  
           if (session.utente.profilo === 'admin')  
                li(role='presentation')  
                     a(href='/libri/nuovo') Nuovo Libro  
Nel caso di utente che accede con il profilo semplice, attiviamo nel menu le rotte (link di richiesta)
  • /libri per visualizzare la lista dei libri
  • /libri/cerca per visualizzare un form di ricerca libro
Nel caso di utente che accede con il profilo admin, viene attivata anche la rotta
  • /libri/nuovo per visualizzare un form di inserimento di un nuovo libro
Il modulo app viene modificanto con l'aggiunta di un nuovo router legato all'indirizzo /libri
 var libri = require('./routes/libri');  
 ...  
 app.use('/libri', libri);  
Il modulo libri fornisce, per ora, la gestione della rotta radice / 
 var express = require('express');  
 var router = express.Router();  
 var serviziLibri = require('../servizi/serviziLibri');  
   
 router.route('/')  
      .get(function(request,response) {  
           var libri = serviziLibri.listaLibri();  
           response.render('listalibri', {libri : libri});  
      });  
   
 module.exports = router;  
e quindi la lista dei libri sarà visualizzata a seguito della richiesta all'indirizzo /libri utilizzando la nuova vista presente in /views/listalibri.jade
 extends ./layout.jade  
   
 block titolo  
      title Lista Libri  
   
 block testata  
      h1.text-info Lista Libri!  
   
 block contenuti-principali  
      if (libri)  
           table(class='table table-striped table-bordered')  
                thead  
                tr  
                     th Codice  
                     th Titolo  
                     th Autore  
                each libro in libri  
                     tr  
                          td #{libro.codiceisbn}  
                          td #{libro.titolo}  
                          td #{libro.autore}  
Il modulo serviziLibri sarà così implementato, sempre utilizzando la tecnica delle funzioni definite ed eseguite immediatamente
 module.exports = (function(){  
      var libri = [  
           {  
                titolo : 'Il signore degli anelli',  
                codiceisbn: 'DRF00223Z',  
                autore: 'John Ronald Reuel Tolkjen'   
           },  
           {  
                titolo : 'Guerra e pace',  
                codiceisbn: 'FTGR44E3',  
                autore: 'Lev Tolstoj'   
           },  
           {  
                titolo : 'Javascript the good parts',  
                codiceisbn: 'ZZ3423DR665',  
                autore: 'Douglas Crockford'   
           }  
      ];  
   
      var listaLibri = function() {  
           return libri;  
      };  
   
      return {  
           listaLibri : listaLibri  
      };  
 })();  
Come si può osservare, continuiamo ancora ad utilizzare una base dati in memoria, tra qualche post sostituiremo il codice presente il questo modulo con quello relativo alla interazione con una base dati.

Nel prossimo post, vedremo come implementare la ricerca, ma ormai dovrebbe essere chiaro, spero, come sia strutturato il codice.

Su https://whispering-peak-1284.herokuapp.com/  potete testare le funzionalità appena implementate.

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

Dopo aver fatto il pull del progetto, ricordatevi di eseguire, se non lo avete mai fatto, npm install nella cartella di progetto;

Se volete eseguire il programma in locale digitate node app.js e puntate il browser all'indirizzo http://localhost:5000.

Alla prox
Ivan

martedì, gennaio 13, 2015

Biblioteca: validiamo i dati inseriti dall'utente

Una applicazione seria non puo' prescindere dalla rognosa validazione dei dati che arrivano al server.

Personalmente, oltre ad una validazione lato client che in effetti può evitare una richiesta inutile al server, preferisco sempre validare anche lato server per questioni di sicurezza.

Se vi ricordate il post sui middleware, la funzionalità di validazione dei dati di un form potremmo implementarla attraverso dei middleware attivati per le rotte (richieste) opportune.

A noi non piace reinventare la ruota, volete che non ci sia un modulo per node.js bello e pronto per la validazione dei dati di un form? Che magari ci permetta di validare in maniera semplice anche campi per indirizzi email o campi numerici?

Ovviamente tale modulo esiste, in realtà ne esiste più di uno, oggi vediamo express-form che ho scelto per la sua semplicità di utilizzo.

Installato nel progetto il modulo express-form, il codice per validare ad esempio il form di login è il seguente presente nel file delle rotte utenti.js, evidenzio solo la parte del codice coinvolta per il login
 var form = require('express-form');  
 var field = form.field;  
   
 var validatoreLogin = form(  
           field('username').trim().required('', 'Inserire il nome utente!'),  
           field('password').trim().required('','Inserire la password!')  
      );  
   
 router.route('/login')  
   .post(validatoreLogin, function(request,response){  
           var utente = undefined;  
           if (!request.form.isValid) {  
               response.render('login', {errors: request.form.errors});  
           }  
           else {  
                utente = serviziBiblioteca.login(  
                     request.body.username,  
                     request.body.password  
                );  
                if (utente) {  
                     request.session.utente = utente;       
                     response.render('home');  
                }  
                else {  
                     response.render(  
                          'login',   
                          { loginFallito : 'Utente non abilitato'}  
                     );  
                }       
           }  
    });  
Il modulo express-form ci fornisce una funzione form che ritorna un middlewar da associare alle richieste che vogliamo validare.

La funzione form, accetta come parametri delle funzioni field per validare i campi del form.

Se spulciate sulla documentazione ufficiale di express-form, troverete degli esempi di validazione per tutti i gusti: in questo caso abbiamo usato il validatore di tipo required che rende il form invalido se il campo in questione non è stato inserito dall'utente.

Una volta definito il middleware, costituito in questo caso dalla funzione validatoreLogin, basta passarlo come primo parametro alla funzione post associata alla richiesta di login.

Il middleware aggiunge all'oggetto request una proprietà form che possiamo testare con l'istruzione
if (!request.form.isValid) e renderizzare la pagina di login passando l'array delle stringhe d'errore presenti in request.form.errors.

Nelle pagine di presentazione, dove occorre visualizzare i messaggi di errore di validazione basterà inserire, dove vogliamo, un codice di questo tipo
 if (errors)  
           ul  
                each error in errors  
                     li.text-danger #{error}   
Vediamo ora come costruire il validatore del form di registrazione
 var validatoreRegistrazione = form(  
    field('username').trim().required('', 'Inserire il nome utente!'),  
    field('password').trim().required('','Inserire la password!'),  
    field('email').trim().required('','Inserire la mail').isEmail('mail invalida')  
  );  
e leghiamo il validatore, che è un middleware, alla richiesta post di registrazione
 router.route('/registrazione')  
   .post(validatoreRegistrazione, function(request,response) {  
           var utente = {};  
           if (!request.form.isValid) {  
                response.render('registrazione', {errors: request.form.errors})  
           } else {  
                utente.username = request.body.username;  
                utente.password = request.body.password;  
                utente.email = request.body.email;  
                utente.profilo = 'semplice';  
                serviziBiblioteca.registra(utente);  
                response.render('login', {  
                     registrazioneOk: 'Ora puoi effettuare il login!'});  
           }  
   });  
Per non avere ripetizioni di codice e grazie alla presenza della istruzione condizionale, conviene inserire il codice della visualizzazione messaggi all'interno del file layout.jade
 ...  
 div.col-md-10  
      block contenuti-principali  
      block validazione  
      if (errors)  
         ul  
          each error in errors  
               li.text-danger #{error}  
 ...  
Su https://whispering-peak-1284.herokuapp.com/ potete testare la validazione così implementata.

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

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

Alla prox
Ivan

Biblioteca: inseriamo le rotte

Cominciamo ad implementare le funzionalità di login e registrazione nella nostra splendida applicazione :)

Ricordiamo che tutto il codice applicativo risiede nel file app.js, che è così costituito
 var express = require('express');  
 var cookieParser = require('cookie-parser');  
 var expressSession = require('express-session');  
   
 var app = express();   
   
 app.set('port', (process.env.PORT || 5000));  
 app.set('views', __dirname + '/views');  
 app.set('view engine', 'jade');  
 app.use(cookieParser());  
   
 app.use(expressSession({  
      secret: 'stringaqualunque',  
      resave: false,  
      saveUninitialized: true    
 }));  
   
 app.use(function(req,res,next) {  
      res.locals.session = req.session;  
      next();     
 });   
   
 app.use(express.static(__dirname + '/public'));   
   
 app.get('/', function(req,res) {  
      res.render('index');  
 });   
   
 app.listen(app.get('port'), function() {  
      console.log("Node app is running at localhost:" + app.get('port'));  
 });      
Per ogni funzionalità che vogliamo implementare dovremo definire un url con la corrispondente funzione di callback per la gestione della richiesta: il file app.js è destinato a crescere in dimensione, se non strutturiamo adeguatamente il codice.

Quello che vogliamo fare è definire un primo modulo per definire e gestire le richieste di login e registrazione, funzionalità riservate agli utenti del nostro sito, per cui creiamo un file utenti.js in una nuova cartella routes che andrà creata allo stesso livello della cartella public
 var express = require('express');  
 var router = express.Router();  
   
 router.route('/login')  
      .get(function(request,response) {  
           response.render('login');  
      });  
   
 router.route('/registrazione')  
      .get(function(request,response) {  
           response.render('registrazione');  
      });  
   
 module.exports = router;  
In questo file abbiamo esportato un modulo (riguardate questo post per rinfrescarvi la memoria) che ha lo scopo di definire un router (potete tradurlo con vigile urbano :)) per instradare le richieste a get /login e get /registrazione, che renderizzano le rispettive viste login.jade e registrazione.jade.

Nel file app.js basterà aggiungere un codice di questo tipo
 var utenti = require('./routes/utenti');  
 app.use('/utente', utenti);  
per attivare richieste del tipo /utente/login e /utente/registrazione

Il file del menù andrà così modificato per rispechiare le rotte definite precedentemente
 if (!session.utente)  
      ul(class='nav nav-pils')  
           li(role="presentation")  
                a(href="/utente/login") Login  
           li(role="presentation")  
                a(href="/utente/registrazione") Registrati!  
Con questo approccio creeremo un altro modulo per definire, ad esempio, le funzionalità per la gestione dei libri: in tal modo i nostri file saranno piccoli e facilmente manutenibili, invece di avere un unico file che sarebbe destinato a crescere al crescere della complessità della nostra applicazione.

Controllate all'indirizzo https://whispering-peak-1284.herokuapp.com/  come si comporta ora la nostra applicazione.

Per completezza vi mostro il codice delle viste login.jade e registrazione.jade che dovranno essere create nella cartella /views
 extends ./layout.jade  
   
 block titolo  
      title Login  
   
 block testata  
      h1.text-info Login  

 extends ./layout.jade  
   
 block titolo  
      title Registrazione  
   
 block testata  
      h1.text-info Registrazione  
Nei prossimi post vederemo come completare queste viste per prevedere i form di login e registrazione con le relative implementazioni.

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

Dopo aver fatto il pull del progetto, ricordatevi di eseguire npm install (se non lo avete mai fatto) 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

lunedì, gennaio 12, 2015

Biblioteca: altro modulo per gestire la logica di business

Oggi implementeremo le funzionalità core di login e registrazione.

Non introdurremo ancora l'interazione con un server di dati per non appesantire l'esposizione, utilizzeremo una struttura dati in memoria per memorizzare le utenze, ma struttureremo il codice in maniera tale da non dover modificare la parte di gestione delle varie richieste presenti nel modulo delle rotte degli utenti: come? Ma utilizzando un altro bel raviolone :).

Prima di mettere il naso nel codice, navigate l'applicazione aggiornata su heroku per capire dove vogliamo andare a parare: https://whispering-peak-1284.herokuapp.com.

Provate a fare il login con username ivan e password saracino, che ha un profilo admin, notate come il menu presenti funzionalità (da implementare) di amministrazione.

Fate il logout per pulire la sessione e entrate nella pagina di registrazione, inserendo una vostra utenza; verrete rediretti alla pagina di login con un opportuno messaggio per effettuare l'autenticazione con le credenziali appena create: rifate il login e notate come cambia il menu e i messaggi di benvenuto presenti nell'header.

Ad ogni riavvio della applicazione, ovviamente tutte le utenze create non ci saranno più, rimarrà valido solo l'utente admin ivan saracino.

Per capire come abbiamo strutturato il codice, date un'occhiata al seguente diagramma di oggetti


Vi ricordo che moduli nel contesto javascript non sono altro che oggetti che espongono particolari proprietà che possono essere anche funzioni.

Il diagramma in alto non dice altro che il modulo app utilizza il modulo delle rotte utenti che a sua volta utilizza il modulo serviziUtenze: grazie a questa separazione di responsabilità tra moduli diversi sarà facile tenere sotto controllo la complessità del nostro progetto.

Il modulo app ha la responsabilità di configurare l'intera applicazione, tramite, ad esempio, l'utilizzo di moduli particolari di express per la gestione delle sessioni o il parsing del corpo delle richieste http: per poter effettuare il post dei form, dovremo includere nel codice del modulo app le seguenti istruzioni
 var bodyParser = require('body-parser');  
 ...  
 app.use(bodyParser.urlencoded({ extended: true }));   
In questo modo sarà possibile estrarre facilmente i parametri mandati in post al server al submit del form.

Il modulo utenti ha la responsabilità di definire le funzioni da eseguire per gestire le varie funzionalità legate agli utenti quali il login e la registrazione.
 var express = require('express');  
 var router = express.Router();  
 var serviziBiblioteca = require('../servizi/serviziUtenze');  
   
 router.route('/login')  
      .get(function(request,response) {  
           response.render('login');  
      })  
      .post(function(request,response){  
           var utente = serviziBiblioteca.login(  
                request.body.username,  
                request.body.password  
           );  
           if (utente) {  
                request.session.utente = utente;       
                response.render('home');  
           }  
           else {  
                response.render(  
                     'login',   
                     { loginFallito : 'Utente non abilitato'}  
                );  
           }  
             
      });  
   
 router.route('/registrazione')  
      .get(function(request,response) {  
           response.render('registrazione');  
      })  
      .post(function(request,response) {  
           var utente = {};  
           utente.username = request.body.username;  
           utente.password = request.body.password;  
           utente.email = request.body.email;  
           utente.profilo = 'semplice';  
           serviziBiblioteca.registra(utente);  
           response.render('login', {  
                registrazioneOk: 'Ora puoi effettuare il login!'});  
      });  
   
 router.route('/logout')  
      .get(function(request,response) {  
           request.session.destroy(function() {  
                response.redirect('/');  
           });  
      });  
   
 module.exports = router;  
Notate dal codice come sia possibile gestire una richiesta sia in get che in post.

Prendiamo ad esempio la richiesta /utente/login e osserviamo come nel caso di una richiesta get venga semplicemente renderizzata la pagina login.jade, mentre nel caso di una richiesta di tipo post venga utilizzato il modulo serviziUtenze per effettuare la vera e propria autenticazione.

Per estrarre i parametri dalla richiesta post, basta usare un codice del tipo request.body.nomeparametro.

Nel modulo serviziUtenze risiede la logica di business vera e propria, in questo caso abbiamo creato un modulo che espone due metodi login e registra che utilizzano un array di oggetti in memoria come base dati delle utenze inizializzato con l'utente admin ivan saracino.
 module.exports = (function(){  
      var utentiRegistrati = [  
           {  
                username : 'ivan',  
                password: 'saracino',  
                email: 'ivan.saracino@gmail.com',  
                profilo: 'admin'   
           }  
      ];  
      var login = function(username,password) {  
           var utentiLoggati = utentiRegistrati.filter(function(utente) {  
                return utente.username === username &&   
                    utente.password === password;  
           });  
           return utentiLoggati[0];  
      };  
      var registra = function(utente) {  
           utentiRegistrati.push(utente);  
           console.log(utentiRegistrati);  
      };  
      return {  
           login : login,  
           registra : registra  
      };  
 })();  
Per organizzare meglio i file, abbiamo previsto la presenza di una cartella servizi in cui memorizzare i file dei moduli della logica di business: non è difficile prevedere la presenza di un ulteriore modulo per la gestione dei libri quando andremo ad implementare tali funzionalità.

Per la generazione del menu dinamico, osserviamo il file menu.jade incluso nel layout
 if (!session.utente)  
      ul(class='nav nav-pils')  
           li(role='presentation')  
                a(href='/utente/login') Login  
           li(role='presentation')  
                a(href='/utente/registrazione') Registrati!  
 else  
      ul(class='nav nav-pils')  
           li(role='presentation')  
                a(href='/utente/logout') Logout  
           if (session.utente.profilo === 'semplice')  
                li(role='presentation')  
                     a(href='#') link semplice 1  
                li(role='presentation')  
                     a(href='#') link semplice 2  
           else  
                li(role='presentation')  
                     a(href='#') link admin 1  
                li(role="presentation")  
                     a(href='#') link admin 2  
Se esiste un utente in sessione, ne verrà controllato il profilo per generare opportunamente i link corretti.

Non abbiamo implementato una validazione dei parametri passati al server, nel prossimo post vedremo come affrontare questo argomento tramite l'utilizzo di un middleware opportuno.

Come al solito, ho aggiornato su github il progetto installato su heroku, in modo che possiate avere sempre 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 la dipendenza aggiuntiva per il modulo body-parser : se volete eseguire il programma in locale digitate node app.js e puntate il browser all'indirizzo http://localhost:5000.

Alla prox
Ivan

giovedì, gennaio 08, 2015

Diamoci un obiettivo per il nuovo anno: una biblioteca con node.js

Ed eccomi qua, dopo un pò di chili presi durante le vacanze, a riprender l'interrotto cammino.

Ho intenzione di modificare l'applicazione presente su heroku sviluppata fino ad ora, in modo da introdurre via via funzionalità classiche come autenticazione, profilazione e accesso a basi dati.

Svilupperemo una applicazione web per la gestione di una biblioteca online, le cui funzionalità sono descritte dal seguente diagramma dei casi d'uso



Un utente che raggiunge il nostro sito, può effettuare le seguenti operazioni
  • Login: inserire il nome utente e la password per accedere al sito
  • Registrazione: creare una nuova utenza inserendo le seguenti informazioni : 
    • nome utente
    • password
    • indirizzo email
Dopo aver effettuato il login, un utente ospite può assumere il ruolo di utente semplice o di un utente amministratore

Un utente semplice puo' effettuare le seguenti operazioni

  • Lista libri: visualizzare la lista dei libri presenti in biblioteca
  • Ricerca libro: ricercare un libro inserendo una parola chiave di ricerca che potrà essere presente nel titolo del libro

Un utente amministratore può fare tutto ciò che può fare un utente semplice e in più potrà effettuare le seguenti operazioni

  • Elimina libro: eliminare un libro dalla base dati
  • Nuovo libro: inserire un nuovo libro nella base dati, un libro ha le seguenti informazioni
    • codice isbn
    • titolo
    • autore

E' esempio molto semplice ma ci darà modo di affrontare e implementare delle classiche funzionalità presenti in una qualsiasi applicazione web.

Implementeremo l'applicazione in maniera incrementale in modo da affrontare le difficoltà un passo alla volta.

Come prima iterazione, impostiamo il layout del sito  (mantenendo quello attuale ma eliminando unicamente la colonna di destra) e sviluppiamo la pagina iniziale index.jade.

Se in sessione non c'è un utente che ha effettuato il login, il menu dell'applicazione sarà costituito dai due link per effettuare il login o la registrazione.

Ecco come si presenterà l'applicazione alla prima iterazione: https://whispering-peak-1284.herokuapp.com/

Nei prossimi post affronteremo l'implementazione delle due funzionalità.

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 (se non lo avete mai fatto) 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




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