Scarica Sisntesi slide Programmazione Web 2018-2019 e più Sintesi del corso in PDF di Programmazione e Tecnologie Web solo su Docsity!
PROGRAMMAZIONE WEB 2018 - 2019
Prof.ssa Bartolini
Web-Browser (client)
Un web browser è un software che serve per avere pagine web, tramite il protocollo http, e visualizzarle. Richiede i documenti XHTML. Come funziona?
- Invia richieste e riceve risposte tramite il protocollo HTTP;
- Interpreta i comandi HTML;
- Visualizza file di tipo diverso
Web-Server
Software che fornisce pagine web mediante protocollo http, vengono reperite dalla macchina su cui risiedono. È in grado di gestire le richieste di trasferimento di pagine web di un client, tipicamente un web browser. La comunicazione tra server e client avviene tramite il protocollo HTTP, che utilizza la porta TCP 80. Memorizza i documenti XHTML.
Form XHTML
L’elemento è “ form”. Gli attributi utilizzati sono “method”, il quale specifica come inviare i dati del form al Web-Server, ed “action”, il quale specifica l’indirizzo di destinazione della richiesta (ovvero dove si trova e come si chiava il file .java).
- method=”post” : I dati del form vengono inclusi nel messaggio di richiesta. Quantità di dati che si possono spedire illimitati, le richieste non possono essere ripetute.
- method=”get” : I dati del form vengono appesi nell’URL. Quantità di dati da inviare limitata in base al browser, di solito 240 caratteri al massimo, le richieste possono essere salavate e ripetute più volte. Poi abbiamo l’elemento “input” il quale specifica i dati da fornire allo script che elabora il form, e sono di vario tipo, ad esempio:
- input type= “submit”
- input type= “reset”
- input type=”radio”
- input type=”text”
- …
Interazione Client-Server Web
L’interazione è sempre basata su protocollo HTTP e l’URL che è l’oggetto della richiesta viene utilizzato per selezionare la risorsa lato server che si vuole utilizzare.
Servlet
È un programma scritto in Java e residente su un server, in grado di gestire le richieste generate da uno o più client, attraverso uno scambio di messaggi tra il server ed i client stessi che hanno effettuato la richiesta. Tipicamente sono collocate all’interno di Application Server o Web Application Server come, ad esempio, Tomcat.
Un servlet container può ospitare più servlet (con relativi alias). Quando una servlet viene invocata per la prima volta, il servlet engine genera un thread Java che inizializza l’oggetto Servlet. Esistono diverse tipologie di servlet, che ereditano da un super-tipo presente nel package javax.servlet. La loro estensione dipende dal protocollo di comunicazione utilizzato. Nel nostro caso sulle HttpServlet presenti nel package javax.servlet.http. Estendendo quest’ultima classe sarà possibile creare servlet in grado di comunicare con i client e rispondere dinamicamente in base alle loro richieste. Il servlet container supporta solo le servlet API. Esso può ospitare più servlet e quando una servlet viene invocata per la prima volta il container genera un thread che inizializza l’oggetto Servlet. Gli oggetti ServletRequest e ServletResponse permettono la ricezione e l’invio di dati al client. L’oggetto ServletConfig viene utilizzato dal container per passare informazioni alla servlet al momento della sua creazione:
- La servlet ottiene questo oggetto tramite il metodo getServletConfig ;
- Contiene un oggetto che implementa l’interfaccia servletContext ;
- Definisce i metodi che una servlet può utilizzare per comunicare con il container;
- Esiste UN SOLO context per ogni web-app; Ciclo di vita di una servlet: 1. Caricamento in memoria della servlet
- Il container invoca il metodo init (invocato una sola volta, inizializzazione delle variabili globali, la servlet è in grado di rispondere alla prima richiesta)
- Vengono gestite le richieste del client col metodo service (invocato ad ogni richiesta del client GET o POST, riceve la richiesta con HttpServletRequest e genera un oggetto di risposta HttpServletResponse che viene passato al web server)
- La servlet viene messa fuori servizio mediante il metodo destroy(). In che modo uso le servelt?
- Supportare richieste multiple in maniera concorrente;
- Aggiornare, eliminare o consultare dati contenuti in un DB remoto tramite protocollo TCP/IP;
- Applicazioni basate sull’autenticazione degli utenti. Il flusso di una servlet è il seguente:
- Un client invia una richiesta (request) per una servlet ad un web application server.
- Qualora si tratti della prima richiesta, il server istanzia e carica la servlet in questione avviando un thread che gestisca la comunicazione con la servlet stessa. Nel caso, invece, in cui la servlet sia già stata caricata in precedenza (il che, normalmente, presuppone che un altro client abbia effettuato una richiesta antecedente quella attuale) allora verrà, più semplicemente, creato un ulteriore thread che sarà associato al nuovo client, senza la necessità di ricaricare ancora la servlet.
- Il server invia alla servlet la richiesta pervenutagli dal client
- La servlet costruisce ed imposta la risposta (response) e la inoltra al server
- Il server invia la risposta al client.
Il cookie che viene creato resta sul computer del client e non può essere inviato ad altri client poiché contiene un ID che viene generato automaticamente. L’aggiunta di un Cookie si chiama sulla response con response.addCookie() , mentre sulla request posso vedere tutti i cookie che mi riguardano tramite il metodo request.getCookies() , che ritorna un array della classe Cookie, Cookie c[] = request.getCookies(), e con il metodo .getName() e .getValue() posso rispettivamente avere il nome della chiave ed il valore di quel particolare Cookie (ovvero nell’esempio, .getValue() mi ritorna il valore di quel particolare cookie, quindi “true”, mentre .getName() mi ritorna il valore della chiave, quindi “login”). In questo caso, se trovo la chiave “login” ed il corrispettivo valore “true”, allora qui dentro so che questo utente è stato loggato e quindi posso effettivamente m ostrargli la pagina protetta. Il server gestisce una session-map dove ogni voce rappresenta una sessione distinta, dove la chiave è un ID per la sessione, mentre il valore è l’oggetto che contiene informazioni associate a quella sessione.
Sessioni
Un oggetto che implementi l’interfaccia HttpSession identifica un utente/browser attraverso diverse richieste e permette di memorizzare informazioni acquisite durante la sessione di navigazione. HttpSession permette alle servlet di:
- vedere e manipolare informazioni sulle sessioni (ID, tempo di creazione, ultimo accesso, …)
- associare oggetti alle sessioni, permettendo la persistenza delle informazioni utente attraverso varie connessioni. Tecnicamente la sessione viene identificata con un codice chiamato session ID, generato in modo casuale dal server e associato al browser dell’utente che si è collegato. Il sessionID viene successivamente scambiato tra client e server, solitamente tramite i cookies, con lo scopo di identificare l’utente e mantenere le sue azioni collegate tra loro. Le informazioni di sessione fanno parte del ServletContext della web-app attuale, quindi le informazioni salvate in un context non sono direttamente visibili all’esterno. Per creare una sessione facciamo: HttpSession session = request.getSession(); Se l’utente si collega per la prima volta, il metodo getSession crea una nuova sessione, altrimenti associa la sessione precedentemente creata. Questo metodo può prendere come parametro anche un booleano true , la sessione verrà creata se già non era presente, false altrimenti. Il getSession non si può usare dopo il commit della risposta, ovvero dopo out.close();.
Quando si vuole interrompere l’utilizzo di una sessione si deve eseguire il metodo invalidate() , che rimuove tutti gli oggetti precedentemente collegati e annulla i riferimenti al session ID. Come può il server riconoscere le richieste di una stessa sessione e associarle correttamente all’oggetto persistente con il contesto di visibilità voluto? Tramite i cookie!
URL Rewriting
Se il browser ha i cookie disabilitati, come faccio a gestire le sessioni sul server? Si deve far appendere all’url un identificativo di sessione mediante l’ URL Rewriting metodo dell’interfaccia HttpServletResponse: String encodeURL(String URL) che appende a qualsiasi URL che gli passiamo l’identificativo della sessione. Quindi se il programmatore richiede la riscrittura degli URL, il container dovrà lavorare seguendo due possibili modalità, ovvero se i cookie sono accettati dal browser, la gestione della sessione avverrà tramite cookie, altrimenti viene attivata la riscrittura degli URL. Quindi sarà conveniente utilizzare sempre il metodo encodeURL per rendere la struttura più robusta possibile. Come funziona dietro le quinte:
- La prima volta che viene chiamato, si manda sia il cookie di sessione che l’URL riscritto;
- Se già dalla seconda chiamata non viene trovato il cookie nella risposta, allora si prosegue con URL rewriting, altrimenti si prosegue con il cookie. Le pagine di risposta generate da sessioni diverse conterranno URL diversi (con diversi ID di sessione appessi all’URL).
Redirezione tra Servlet
Esistono due tipi di redirezione:
- La prima lato client permette solo la redirezione e vale con qualsiasi URL (anche esterno alla webapplication) con il metodo sendRedirect().
- La seconda lato server invece permette sia la redirezione che l’inclusione e vale solamente dentro la stessa web application, basato sull’interfaccia RequestDispatcher, riceve richieste da un client e le inoltra a qualsiasi risorsa sul server. sendRedirect(URL) è un metodo di HttpServletResponse ed ha un solo argomento ovvero l’URL di destinazione che può essere esterno al server, relativo al server (inizia con lo “/”) o relativo (NON inizia con lo “/”). Viene invocato dal web-server per costruire la risposta http la quale conterrà un header di redirezione verso la nuova locazione e poi verrà interpretato dal browser.
Può essere usata solo per richieste di tipo GET (per richieste POST solo nelle JSP).
§ errorPage : il cui valore è la pagina da visualizzare quando c’è un errore di esecuzione; § isErrorPage : è un booleano che si usa quando si usa errorPage , se true ci sarà una variabile in più ovvero exception che ci permette di sapere qual è l’eccezione che è stata scatenata. § ed altri … o <%@ include file=”resource”%> ha un solo attributo, ovvero file per importare contenuti esterni/package. Se il file inizia con / allora si intende essere relativo al context-root, altrimenti è relativo al punto in cui si trova il file JSP; o <%@ taglib uri=”taglibURI” prefix=”taglibPrefix”%> per importare un tag library.
- Azioni : sono codificate in un linguaggio di programmazione e sono specificate sotto forma di: o Scriplet: sono blocchi di codice eseguiti nel contesto della pagina per creare pagine dinamiche. Interagiscono con gli elementi della pagina e altre componenti per creare pagine dinamiche; o Azione standard: sono azioni per controllare il comportamento del container tramite tag, come ad esempio <jsp:useBean>,… o Tag personalizzati: consente la creazione di nuovi tag da parte del programmatore. e come le servlet, fanno uso di oggetti request e response ed hanno accesso a tutti i dati della richiesta. Ciclo di vita di una pagina JSP:
- La prima volta che viene invocata la pagina, il container la traduce in una servlet, che viene compilata e mandata in esecuzione (esecuzione del metodo _jspInit ).;
- Il container invoca il metodo _jspService ad ogni richiesta successiva della medesima JSP. Possono presentarsi degli errori:
- Al momento della traduzione, ovvero si verificano nel momento in cui viene generata la servlet corrispondente alla JSP;
- Errori al momento della richiesta, ovvero si verificano durante l’elaborazione della richiesta. Il commento JSP NON si usa all’interno delle scriplet e NON è visibile al client. Il commento XHTML NON si usa all’interno di scriplet ed è visibile al client. Il commento del linguaggio di scripting si usa SOLO all’interno di scriplet e NON è visibile all’utente. Le Espressioni sono delimitate da “<%= %>” e contengono espressioni Java che vengono valutate quando il client richiede la pagina che le contiene ed il container converte il risultato di un’espressione in un oggetto String e lo invia come output nella risposta.
Le Dichiarazioni sono delimitate da “<%! %>” e consentono la definizione di variabili e metodi attraverso la sintassi in Java. Le variabili diventano attributi della classe servlet che rappresenta la pagina JSP. I metodi così dichiarati corrisponderanno ai metodi della classe servlet che rappresenta la classe JSP. L’oggetto Page rappresenta l’istanza corrente della servlet corrispondente alla pagina JSP. Il suo tipo è HTTPJspPage e può essere utilizzato per accedere a tutti i metodi definiti nella servlet. L’oggetto PageContext fornisce informazioni sul contesto di esecuzione della JSP rappresentando l’insieme degli oggetti impliciti di una JSP. È utile per il custom tag. L’oggetto Application fornisce informazioni sul contesto di esecuzione della JSP rappresentando la web-app a cui appartiene la JSP, consente di interagire con l’ambiente di esecuzione. L’oggetto Exception è connesso alla gestione degli errori. Non è automaticamente disponibile in tutte le pagine ma solo nelle ErrorPage (quelle dichiarate con l’attributo errorPage impostato a true).
Servlet da pagine JSP
Aprendo una servlet generata da una pagina JSP, situata in “ /Tomcat/apache-tomcat- 9.0.17/work/Catalina/localhost/nomeWebApp/org/apache/jsp/nome.java ”, si notano delle differenze rispetto alle normali servlet. Innanzitutto la classe di partenza non è HttpServlet ma HttpJspBase. Ci sono 2 diverse possibilità per l’inizializzazione e la deallocazione: o _jspInit e _jspDestroy le quali sono create dal JSP container e non modificabili dall’utente. o jspInit e jspDestroy che possono essere specificate dentro una pagina JSP, usando le dichiarazioni JSP. Il metodo di servizio non è né service ne doGet o doPost, ma bensì _jspService , con i due soliti argomenti per richiesta e risposta. I contenuti degli scriplet <%! String name%> di dichiarazione sono ricopiati pari-pari all’inizio della classe, mentre tutti gli altri scripting elements e template text, vengono messi dentro il _ jspService. Il template text è messo riga per riga dentro le chiamate out.write(template). Il contenuto delle espressioni JSP, <%= expr %> è passato direttamente a out.print(). Il contenuto delle scriplet <% … %> viene preso così com’è e ricopiato ed eventuali variabili dichiarate dentro le scriplet diventano variabili locali di _jspService
name=”beanName”property=”propertyName” param=”parameterName”>. name è il nome dell’istanza, ovvero l’ id specificato nell’ useBean. Per poter definire le proprietà del Bean attraverso dati provenienti da un form si usa: <jsp: setProperty name=”beanName”property=””>* dove “*” il metodo setProperty fa corrispondere tutti i parametri dell’oggetto richiesta ai metodi del setBean. Una volta che il Bean è stato creato e settato è possibile leggerlo mediante il tag <jsp:getProperty name=”beanName”property=”propertyName”>. Le espressioni JSP (<%=expr%>) non sono sempre ammesse nelle azioni standard! Possiamo usare le espressioni solo in name di jsp:setProperty , value di jsp:setProperty e di jsp:param , page di jsp:include e di jsp:forward. Ad esempio in <jsp:useBean id=’<%=expr%>’ …> NO!!!.
File XML per pagine JSP
Il tag <welcome-file> viene utilizzato per indicare una risorsa di default da invocare quando la richiesta punta direttamente alla context-root.
Protezione delle risorse – Autenticazione
Se un utente vuole accedere ad una pagina JSP protetta, deve essere autenticato e se nome utente e password non sono corretti verrà generata una pagina di errore. Abbiamo due tipi di autenticazione:
- Dichiarativa: l’esempio classico è quello del manager che quando si accede la prima volta abbiamo settato username e password dal file .xml ed è il servlet container che gestisce i nomi, le password ed i ruoli. Quindi si utilizza il web.xml per dichiarare che delle risorse sono disponibili solo ad utenti autenticati e che hanno un certo tipo di ruolo. Quando viene richiesta una risorsa protetta, il server richiede all’utente username e password, li confronta con un set predefinito e, automaticamente, tiene traccia degli utenti già autenticati. Questo processo è completamente trasparente alle servlet e alle pagine JSP.
- Programmata: implica la gestione diretta da parte delle pagine JSP o servlet. Abbiamo tre tipi di autenticazione dichiarativa :
- BASIC: quando un utente cerca di accedere ad una risorsa protetta, il servlet container invia automaticamente una finestra di “pop up” di richiesta di username e password. Le password vengono trasmesse mediante codifica base64 ;
- FORM: funziona come il BASIC ma viene visualizzata la pagina di login anziché una finestra di dialogo, nel caso in cui ci sia un errore nell’autenticazione viene specificata una pagina di errore. Le password vengono trasmesse mediante codifica base64. Bisogna implementare una pagina di login ed una di errore. Nel form i name devono essere j_username, jpassword, j_security_check (questo in form action=” “ ). FORM è l’unica che crea una sessione;
- DIGEST: quando un utente cerca di accedere ad una risorsa protetta, il servlet container invia automaticamente una finestra di “pop up” di richiesta di username e password. Le password vengono trasmesse mediante codifica hashing. e la scelta di tale meccanismo è scelto mediante i tag <login-config> e <auth-method> (il quale è facoltativo, poiché di default setta BASIC) In che modo dichiaro le risorse da proteggere? In <web-resource-collection> diciamo quali sono le risorse da proteggere, quindi può essere una servlet, una pagina html, un’immagine e così via. Ci possono essere più di uno di questi tag. Al suo interno ci vogliono due tag semplici (al suo interno c’è direttamente del testo):
- <web-resource-name> : ce ne deve essere solo uno, contiene il nome di una risorsa web.
- <url-pattern> : ce ne possono essere più di uno, indica il path delle risorse che si vogliono proteggere. In <auth-constraint> diciamo quali ruoli possono accedere a tali risorse definite in web- resource-collection. Di questo tag c’è ne può essere uno ed è facoltativo metterlo. Al suo interno va annidato un tag semplice ovvero <role-name> (il quale può essere più di uno) e contiene il ruolo, contenuto in tomcat-users.xml , al quale si applica la restrizione.
Tag Personalizzati
Le funzionalità dei tag vengono definite all’interno di classi java che implementano l’interfaccia Tag.
- Bisogna definire per ogni tag, una classe handler del tag che ne implementi le funzionalità. Questa classe contiene la logica del tag ed implementa l’ interfaccia tag. In generale si estendono le classi TagSupport (tag che non elaborano il contenuto del proprio body) o BodySupport (tag che elaborano il contenuto del proprio body). L’interfaccia tag definisce 6 metodi: - void setPageContext(PageContext) : associa il contenuto della pagina al tag in questione; - void setParent(Tag) : ad una classe Tag dico quale sia il suo genitore; - int doStartTag() throws JspException : viene invocato subito dopo i primi due e restituisce un valore intero che condiziona l’elaborazione del tag ( SKIP_BODY : il corpo del tag non viene considerato, EVAL_BODY_INCLUDE : il corpo del tag deve essere trascritto invariato, EVAL_BODY_BUFFERED : il corpo del tag viene elaborato e viene creato un oggetto BodyContent utilizzato come oggeto out ); - int doEndTag() throws JspException : viene chiamato in corrispondenza del tag di chiusura e restituisce un valore intero che condiziona l’elaborazione della parte della pagina che segue il tag (SKIP_PAGE: la parte di pagina dopo viene ignorata, EVAL_PAGE: la parte di pagina viene considerata); - void release : rilascia le risorse dell’handler tag ;
- Possono contenere codice iterativo;
- Possono manipolare il contenuto del loro corpo. L’interfaccia BodyTag estende l’interfaccia Tag definendo questi metodi: o void setBodyContent() : configura le proprietà dell’oggetto BodyContent. Questo metodo viene invocato solo se doStartTag() restituisce EVAL_BODY_BUFFERED. METODO RICHIAMATO DAL JSP CONTAINER. o void doInitBody() : viene invocato dal container dopo setBodyContent() e prima che il corpo del tag venga valutato per la prima volta. Questo metodo viene invocato solo se doStartTag() restituisce EVAL_BODY_BUFFERED. o void doAfterBody() : è il metodo in cui bisogna definire il comportamento per tag che devono modficare/elaborare il corpo. Questo metodo restituisce un valore intero che condiziona l’elaborazione del tag: § SKIP_BODY: il corpo del tag non deve essere considerato. § EVAL_BODY_AGAIN: il corpo del tag deve essere valutato nuovamente. o void doEndTag(): viene chiamato come per l’interfaccia Tag quando il container incontra il tag di chiusura. Tale metodo restituisce un valore intero che condiziona l’elaborazione della parte di pagina che segue il tag: § SKIP_PAGE: la parte di pagina oltre il tag di chiusura viene ignorata. § EVAL_PAGE: la parte di pagina oltre il tag di chiusura viene considerata.
La classe BodyTagSupport
La classe BodyTagSupport estende la classe TagSupport ed implementa l’interfaccia BodyTag. Tale classe restituisce nuovi metodi:
- BodyContent getBodyContent(): restituisce il contenuto del corpo di un tag;
- JspWriter getPreviousOut(): restituisce lo scrittore associato al tag genitore o la variabile implicita out se il tag è di livello superiore.
Utilizzo di BD da Servlet/JSP
Si fa uso di API che ci forniscono i metodi per l’apertura di una connessione con il DB, il recupero di dati e l’aggiornamento di dati.