Diario per Applicazioni Web, a.a. 2013/14

Matteo Vaccari > Applicazioni Web

Sommario

Lezione 1, 2014-03-07

Argomenti: HTTP, URI, URL (slides)

Esercizi

Per eseguire questi esercizi usa Linux oppure un Mac. Non Windows. Se non hai una macchina Linux, puoi usare una macchina virtuale: scarica VirtualBox e installaci sopra Ubuntu.

Installa netcat (nc).

Esercizi svolti in laboratorio

Abbiamo usato nc per interrogare un server che permette di giocare a "hangman" (in Italiano e' il gioco dell'"impiccato" o del "prigioniero"). Consiste nell'indovinare una parola di cui conosciamo soltanto la lunghezza. Possiamo proporre una lettera; se la parola da indovinare contiene questa lettera, verra' mostrata per esempio cosi': ***e**e

L'indirizzo del server: http://aw-hangman.herokuapp.com/

Esegui il seguente esercizio, seguendo le istruzioni passo a passo. Nota: in laboratorio abbiamo usato il tool netcat, ma sulla versione che ho pubblicato su Heroku netcat non funziona. Lo sostituiamo con telnet. (Se vuoi sapere il motivo, e' che formalmente la fine linea va specificata con due byte: CR e LF. Con netcat, il tasto invio manda soltanto LF. Telnet invece li invia entrambi).

Questo server non ha le istruzioni, ma dalle sue risposte possiamo dedurre come interrogarlo.

Lo interrogo con

telnet aw-hangman.herokuapp.com 80
GET / HTTP/1.1
host: aw-hangman.herokuapp.com
<invio>

Quello che ho scritto sopra significa: eseguo il comando telnet aw-hangman.herokuapp.com 80 sul terminale di Unix, e poi scrivo o copiaincollo le righe seguenti. La scritta <invio> significa che per completare il messaggio devo mandare una riga vuota.

Cosi' facendo ottengo

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Content-Length: 32
Server: Jetty(6.1.26)

{"index":"/",
"users":"/users"}

Il formato di questa risposta si chiama JavaScript Object Notation (JSON). Viene spesso utilizzato per realizzare servizi web. Questa risposta mi dice che ci sono due url che posso interrogare: http://aw-hangman.herokuapp.com/ (la url che ho appena interrogato) e http://aw-hangman.herokuapp.com/users.

Se proviamo a interrogare quest'ultima che cosa otteniamo?

telnet aw-hangman.herokuapp.com 80
GET /users HTTP/1.1
host: aw-hangman.herokuapp.com
<invio>

HTTP/1.1 405 Method Not Allowed
Content-Type: application/json; charset=UTF-8
Content-Length: 103
Server: Jetty(6.1.26)

{"status_code":405,"status":"Method not allowed",
"description":"Use POST on /users to create a user"}

Questo ci dice che il metodo GET non e' ammesso per questa url. Che cosa facciamo allora? Seguiamo il suggerimento. Quello che vogliamo fare a questo punto e' creare un nuovo utente.

telnet aw-hangman.herokuapp.com 80
POST / HTTP/1.1
host: aw-hangman.herokuapp.com
<invio>

HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=UTF-8
Content-Length: 89
Server: Jetty(6.1.26)

{"status_code":400,"status":"Bad Request",
"description":"Parameter 'name' is required"}

Hmmm. Apparentemente dobbiamo aggiungere un parametro "name". Proviamo:

POST / HTTP/1.1
host: aw-hangman.herokuapp.com
Content-Type: application/x-www-form-urlencoded
Content-length: 10

name=pippo

Il formato della richiesta POST e' come sopra. I parametri vengono passati nel body. Otteniamo

HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=UTF-8
Content-Length: 93
Server: Jetty(6.1.26)

{"status_code":400,"status":"Bad Request",
"description":"Parameter 'password' is required"}

Aha! Manca un'altro parametro. Aggiungiamolo:

POST / HTTP/1.1
host: aw-hangman.herokuapp.com
Content-Type: application/x-www-form-urlencoded
Content-length: 26

name=pippo&password=secret

Questa volta otteniamo:

HTTP/1.1 303 See Other
Content-Type: application/json; charset=UTF-8
Location: http://aw-hangman.herokuapp.com/users/551350b3
Content-Length: 71
Server: Jetty(6.1.26)

{"status_code":303,"status":"See other",
"location":"/users/551350b3"}

Una redirezione! Questo e' un pattern ricorrente. Come studiamo nell'articolo GET After POST, quando una POST crea con successo una nuova risorsa, il server ci restituisce una redirezione alla URL della risorsa appena creata. Come possiamo vedere, un nuovo utente e' stato creato con id 551350b3.

AGGIORNAMENTO 3/4/14 questo comportamento è cambiato; ora il server restituisce sempre un 201 Created.

Se seguiamo la redirezione otteniamo

GET /users/551350b3 HTTP/1.1
host: aw-hangman.herokuapp.com
<invio>

HTTP/1.1 403 Forbidden
Content-Type: application/json; charset=UTF-8
Content-Length: 181
Server: Jetty(6.1.26)

{"status_code":403,"status":"Forbidden",
"description":"You don't have the permission to access the requested resource. It is either read-protected or not readable by the server."}

Direi che le ragioni del server sono comprensibili. Abbiamo creato un utente con una password, e ora per accedervi dobbiamo fornire la password. Ora ATTENZIONE: il server si aspetta un parametro sulla query string, ma QUESTA NON E' UNA BUONA SOLUZIONE. E' una semplificazione per fini didattici. In un vero servizio web, la password NON DEVE MAI VIAGGIARE NELLA QUERY STRING.

GET /users/551350b3?password=secret HTTP/1.1
host: aw-hangman.herokuapp.com
<invio>

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Content-Length: 84
Server: Jetty(6.1.26)

{"id":"551350b3",
"prisoners":"/users/551350b3/prisoners",
"url":"/users/551350b3"}

Da questo momento in poi prosegui l'esercizio da solo. Il risultato della ricerca precedente ci da' una nuova url da esplorare. Apparentemente ogni user ha dei prisoners. Il meccanismo da usare sara' lo stesso: con GET vedremo che la lista dei nostri prisoners e' vuota. Ne potremo creare uno facendo una POST alla url dei prisoners del nostro utente. Se la POST ha successo, ci verra' restituita la URL del nostro prigioniero, che avra' la forma /users/123/prisoners/456. Facendo GET a questa url osserviamo lo stato del gioco. Facendo POST a questa url, potremo tentare di indovinare, una lettera per volta.

Riesci a vincere? :-)

Da studiare

Materiali per approfondimenti facoltativi

Lezione 2, 21/03/2014

Argomenti: HTML, Web Standards, Character Encodings (slides)

Da studiare

Approfondimenti facoltativi

Esercizio svolto in laboratorio

Costruire alcune form HTML che ci permettano di giocare a hangman in maniera più agevole. Nota bene: la interfaccia utente che costruiamo qui è molto rozza, ma è il massimo che possiamo fare senza programmare.

Primo passo: creare un utente

Scrivi un file hangman.html che contenga:


<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Hangman Client</title>
  </head>
  <body>
    <h1>Hangman!</h1>

    <form action="http://aw-hangman.herokuapp.com/users" method="post">
      <p><label for="name">Name</label><input type="text" name="name" value=""/></p>
      <p><label for="password">Password</label><input type="text" name="password" value=""/></p>
      <p><input type="submit" value="Create user"/></p>
    </form>
  </body>
</html>

Apri il file in un browser, riempi i campi e premi "Create user". Se tutto funziona correttamente, avremo inviato una POST al server, il quale risponderà con una redirezione. Nel browser vedremo qualcosa tipo:

Messaggio di Unauthorized da parte dell applicazione

Che cosa è successo? È successo che il browser ha cercato di fare la redirezione a http://aw-hangman.herokuapp.com/users/54ea83d1, ma non ha fornito il parametro "password" sulla query string. Di conseguenza, l'applicazione ha risposto che non siamo autorizzati a visitare questa url. Ma la creazione dell'utente ha avuto successo! Ce ne accorgiamo perché la url nella barra degli indirizzi del browser è cambiata. Ora c'è l'id dell'utente appena creato.

Ricorda quello che abbiamo detto alla lezione 1: dopo il post, usualmente abbiamo una redirect!

Per confermare che quello che dico è vero, prova ad aggiungere la password nella query string. Vediamo così il nostro user.

Il nostro user

A questo punto sappiamo l'id del nostro utente e sappiamo la password. La strada è aperta: bisogna scrivere un'altra form per creare un prisoner. Siamo costretti a cablare l'id dell'utente nel codice html, il che significa che se volessimo usare un'altro utente, dovremmo cambiare il codice html. Come dicevo, questa interfaccia utente è rozza, ma è il massimo che possiamo fare dato quello che sappiamo fino ad ora.

Scrivi una nuova form e crea un prisoner. Ricora che in tutte le form che fai da questo momento in poi dovrai aggiungere un campo nascosto che fornisce la password. Ricorda anche di non spaventarti se l'applicazione risponde con un codice 403. I casi sono due: o la tua richiesta non è andata a buon fine, e non e' stato creato niente; oppure il prisoner è stato creato, e lo vedi da come cambia la barra degli indirizzi del browser.

Una volta che hai creato un prisoner, devi trovare la parola! Crea un'altra form che ti permetta di inserire agevolmente le lettere. Anche qui: l'output dell'applicazione sul browser sarà sempre un messaggio di errore. Per vedere lo stato del prigioniero, ricarica la url nel browser aggiungendo la password sulla barra degli indirizzi.

Esercizi

  1. Scrivere un documento HTML che aperto nel browser abbia questo aspetto:
    Hello, World!
  2. Verificare che il codice dell'esercizio precedente sia HTML valido per mezzo del servizio validator.nu.
  3. Realizzare in HTML e validare con validator.nu:
    Hello, World!
  4. Nell'esercizio precedente, invece di scrivere “Perch&eacute;” provate a scriverlo con il carattere “é” che si trova sulla tastiera. Verificate che cosa succede nei vari casi:
  5. Realizzare in HTML e validare con validator.nu:
    Rendering di una pagina HTML di esempio
    (La url dell'immagine è http://www.ruby-lang.org/images/logo.gif)
  6. Realizzare in HTML e validare con validator.nu:
    Form html
  7. Verificare che mettendo o togliendo la dichiarazione <!DOCTYPE html> in testa al documento, l'aspetto grafico della pagina cambia leggermente.

Esempi di domande d'esame

Lezione 3, 2014-03-27

Argomenti: Il linguaggio JavaScript. Programmazione funzionale, closures. Programmazione a oggetti; prototipi; JSON. Manipolazione del DOM. Gestione degli eventi. Test unitari in JavaScript. (slides)

Studiare:

  1. Studiare i capitoli 1-6 di Pro JavaScript Techniques di John Resig. La versione pdf si può comprare per $32 circa.
  2. Guardare la presentazione JavaScript Survival Guide di Giordano Scalzo

Esercizio svolto in laboratorio

Scaricare il progetto .

L'obiettivo e' realizzare una pagina web che gioca a Hangman. In questo esercizio non c'e' il server; e' tutto implementato all'interno del file html.

Procedimento:

  1. Apri hangman.html nel browser e verifica che premendo il bottone "New game", i campi "prisoner" e "remaining" vengono popolati.
  2. Apri hangman_test.html nel browser e verifica che il test fallisce.
  3. Modificare scripts/hangman.js per fare passare il test, che si trova in scripts/hangman_test.js.
  4. Quando il primo test passa, scommenta il successivo da scripts/hangman_test.js; verifica che non passa, e poi modifica hangman.js per farlo passare.
  5. Prosegui cosi' fino a quando tutti i test non passano.
  6. A questo punto torna a hangman.html. Modificalo facendo in modo che quando si preme il tasto "Guess", il messaggio "guess" viene inviato all'oggetto Prisoner, e i campi "Remaining" e "Prisoner" vengano aggiornati di conseguenza.

A questo punto il gioco funziona. Per migliorarlo, potresti:

Lezione 4, 2014-04-03

Argomenti: Ajax e jQuery (slides su Ajax e slides su jQuery da pagina 4 a 27 comprese.)

Studiare la documentazione della funzione $.ajax di jQuery

Esercizio svolto in laboratorio

Scaricare il progetto . La maniera consigliata di farlo è tramite il comando git clone https://github.com/xpmatteo/aw-hangman-client

L'obiettivo è di creare un client ajax per il server hangman della prima lezione. Esegui questi passi; a ogni passo verifica di avere ottenuto il risultato desiderato. Carica sempre la pagina html tenendo aperta la console JavaScript.

  1. Per riscaldamento, nascondi l'immagine animata che gira, usando un comando jQuery.
  2. Inserisci un comando ajax che carichi nell'elemento che ha id output il risultato di interrogare in get la url http://aw-hangman.herokuapp.com/, non appena la pagina si carica. Ti servirà qualcosa tipo

function stringify(data) {
  return JSON.stringify(data, null, 2)
}

function on_receive_index(data) {
  $('#output').text(stringify(data))
}

function on_failure(data) {
  $('#output').text('*** ERROR ***\n' + stringify(data))
}

$.ajax({
  url: 'http://aw-hangman.herokuapp.com/',
  method: "GET",
  success: on_receive_index,
  error: on_failure,
  data: {},
})
  1. Ora fai in modo che il caricamento venga fatto non quando si carica la pagina, ma quando si preme il bottone "Create User". Devi attaccare una funzione all'evento "submit" della form. Qualcosa tipo $("#make-new-user").submit(function() { ... })

    Ricordati di restituire "false" come ultima cosa nell'event handler, altrimenti la pagina si ricarica per via del comportamento di default del submit!!!

  2. I passi precedenti erano tutti di riscaldamento. Adesso facciamo la prima azione vera con il server: modifichiamo il codice che abbiamo scritto finora per fare in modo che quando si preme il bottone "Create User", venga creato un nuovo utente nel server hangman. Per fare questo dobbiamo:

    Nota che ho modificato il server; prima faceva la redirezione dopo la post. Ma ho scoperto che la redirezione viene seguita automaticamente da Ajax, cosa che ci viene scomoda. Ora invece dopo una post che ha avuto successo, restituisce 201 Created.

    Se la creazione ha avuto veramente successo, dovresti essere in grado di vedere il tuo utente chiedendolo direttamente con il browser alla url http://aw-hangman.herokuapp.com/user/abcdef?password=lamiapassword.

    Nota che per estrarre il valore dai campi di testo, occorre selezionarli, ad esempio mettendo a ciascuno dei due un opportuno id, ed usando la funzione val: ad esempio, $('#password').val()

  3. A questo punto dovresti essere in grado di proseguire da solo: quando crei un utente, memorizza la sua url e la sua password in due variabili globali. Le userai per il passo successivo.
  4. Fai in modo che quando si preme il bottone New Game! venga creato un nuovo gioco. Fai in modo che i bottoni con le lettere ti consentano di completare una partita. :-)

Aggiunta 2014.04.06

Alcune risorse aggiuntive per imparare JavaScript.

Lezione 5, 2014-04-11

Argomenti: Cascading Style Sheets (CSS), applicare stili a documenti HTML, creare layout di pagina con CSS (slides).

Esercizi fatti in laboratorio

Scarica il repository degli esercizi. Le istruzioni sono dentro.

Da studiare

Studiare questi articoli:

Esempi di domande d'esame

Non è necessario imparare a memoria tutte le proprietà e i loro possibili valori. E' necessario però conoscere per lo meno le proprietà che usiamo negli esempi visti a lezione e nell'esercizio (vedi più avanti).

Approfondimenti facoltativi

Lezione 6, 2014-04-16

Argomenti: Servlet API. Discussione su servlet container piuttosto che server embedded. (slides)

Esercizio svolto in laboratorio: https://github.com/xpmatteo/aw-servlet-exercises (le istruzioni sono nel README)

Letture facoltative: The Unix Way vs. the Java Enterprise Way

Lezione 7, 2014-05-16

Argomenti: come installare un'applicazione in produzione (deployment). Uso dei sistemi di version control. Uso di Maven, Git ed Heroku.

L'esercizio è descritto nel README di questo progetto: https://github.com/xpmatteo/aw-single-page-webapp-exercise.

Da conoscere: i comandi di git init, status, add, commit, log, diff, clone, push, pull. Studiare Git Basics. Guardare il video Git Basics.

Da conoscere: i comandi di maven clean, compile, package. Studiare il tutorial di maven. Puoi, se ti serve, cercare altre informazioni sul getting started di maven.

Studiare l'introduzione a Heroku.

Lezione 8, 2014-05-23

Argomenti: architettura server-side di una Single Page Web Application.

L'esercizio è descritto nel README di questo progetto: https://github.com/xpmatteo/aw-supermarket-checkout-stage-0-mvc.

Note sul design del lato server di questa applicazione. Il codice dell'applicazione si puo' leggere in questo ordine.

Lezione 9, 2014-05-30

Argomenti: persistenza. Come usare il database in Java. Il pattern "repository".

Slides: 2014-aw-lezione-09.pdf.

L'esercizio è descritto nel README di questo progetto: https://github.com/xpmatteo/aw-supermarket-checkout-stage-1-persistence.

Lezione 10, 2014-06-04

Argomenti: sessioni, HTTP cookies, autenticazione, autorizzazione.

Che cos'è una sessione e perché è utile.

Perché è importante salvare solo lo user-id in session.

Che cosa sono e come funzionano gli HTTP cookie.

Come autenticare un utente. Come salvare una password in maniera sicura nel database.

L'esercizio è descritto nel README di questo progetto: https://github.com/xpmatteo/aw-supermarket-checkout-stage-2-authentication.

Lezione 11, 2014-06-18

Argomenti: uso dei template in JavaScript. La libreria di templating Mustache. Usare il pattern "model-view-controller", e in particolare il pattern "observer" per organizzare meglio il codice lato client.

L'esercizio è descritto nel README di questo progetto: https://github.com/xpmatteo/aw-supermarket-checkout-stage-3-client-side-mvc.

Lezione 12, 2014-06-20

Argomenti: ripasso e riepilogo, domande e risposte. Abbiamo parlato di come affrontare lo sviluppo dell'elaborato. Una possibile strategia è di partire dall'interfaccia utente, simulando le funzioni del server con uno stub. Ad esempio

var mastermind_server = {
  authenticate: function(id, password) {
    return true;
  }

  on_user_try: function(code) {
    // return  + + -
    return {
      plus_signs: 2,
      minus_signs: 1,
    }
  }

  // ...
};

Si comincia facendo funzionare l'applicazione con questo stub, e poi progressivamente si vanno a sostituire le funzioni stub con funzioni vere che accedono al server tramite chiamate Ajax.

Per la soluzione del tema, si puo' prendere spunto per il lato server dalla mia implementazione di hangman. Per il lato client, da tutti gli esempi visti nelle lezioni passate.

Ci sono due possibili strategie di sviluppo; una e' di implementare la logica del gioco Mastermind nel server (seguendo l'esempio di Hangman.) L'altra e' di implementare la logica del gioco in JavaScript, ed utilizzare il server unicamente come servizio di persistenza. Entrambe le strategie sono valide.