Argomenti: HTTP, URI, URL (slides)
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).
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? :-)
Materiali per approfondimenti facoltativi
Argomenti: HTML, Web Standards, Character Encodings (slides)
Approfondimenti facoltativi
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.
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:
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.
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.
<meta charset="UTF-8"/>
nella head
del documento<meta charset="ISO-8859-1"/>
<!DOCTYPE html>
in testa al documento, l'aspetto grafico della pagina cambia leggermente.
id
e class
?Argomenti: Il linguaggio JavaScript. Programmazione funzionale, closures. Programmazione a oggetti; prototipi; JSON. Manipolazione del DOM. Gestione degli eventi. Test unitari in JavaScript. (slides)
Studiare:
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:
A questo punto il gioco funziona. Per migliorarlo, potresti:
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
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.
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: {},
})
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!!!
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()
Aggiunta 2014.04.06
Alcune risorse aggiuntive per imparare JavaScript.
Video su JavaScript (facoltativo:) Douglas Crockford on JavaScript - Act III: Function the Ultimate
Un libro su JavaScript gratuito in alternativa a Resig: http://eloquentjavascript.net/
(Facoltativo) Una maniera divertente di imparare JavaScript: superare le sfide di http://coderbyte.com/CodingArea/Challenges/
(Facoltativo) In alternativa al precedente: JavaScript Koans
Argomenti: Cascading Style Sheets (CSS), applicare stili a documenti HTML, creare layout di pagina con CSS (slides).
Scarica il repository degli esercizi. Le istruzioni sono dentro.
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
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
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.
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.
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.
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.
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.
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.