Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Filtri e sessioni

Le sessioni in Ruby on Rails: come mantenere persistenti i dati di collegamento
Le sessioni in Ruby on Rails: come mantenere persistenti i dati di collegamento
Link copiato negli appunti

Abbiamo oramai capito come sia possibile validare dei modelli e come sia semplice verificare i dati di un autore per permettergli di inviare dei messaggi e di creare nuovi topic di discussione. Ma la nostra Web application è ancora ignara di tutto questo, poiché le informazioni sul login non sono salvate da nessuna parte. La soluzione per mantenere dei dati persistenti risiede nell'uso delle sessioni.

HTTP è un protocollo stateless, ovvero non mantiene una memoria delle informazioni registrate tra una pagina e l'altra. L'unico modo per tenere traccia di cosa ha fatto l'utente è utilizzare delle sessioni, ovvero mantenere delle informazioni lato server e far sì che l'utente si porti dietro (tramite un cookie) soltanto i dati necessari a recuperare queste informazioni. In Rails, come in altre piattaforme, questo è astratto tramite il meccanismo delle sessioni. 

È possibile accedere alla sessione corrente tramite il metodo session, disponibile automaticamente in tutti i controller così come params o request visti in precedenza. L'oggetto accessibile tramite questo metodo è ancora una volta una collezione di coppie chiave/valore, dove il valore può essere un qualsiasi oggetto Ruby (ma attenzione: se gli oggetti sono "semplici" essi possono essere usati direttamente, mentre in alcuni casi potrebbe essere necessario insegnare a Rails come recuperarli, fate riferimento alla documentazione per i dettagli). 

Quello che è necessario per noi dunque, è di salvare le informazioni di login, ed in particolare, il nome con cui un utente è entrato nel sistema, per poi riutilizzarlo in seguito. 

Fortunatamente, abbiamo un metodo che è perfettamente adatto a questo scopo, ovvero logged in UserController. Per memorizzare le informazioni sarà sufficiente cambiarlo in questo modo:

def logged
   session['login']=@author.name
   redirect_to(:controller=>"home")
end

come vedete è semplicissimo modificare questi parametri, ed è altrettanto semplice accedervi. Il punto in cui avremo bisogno di recuperare queste informazioni è il metodo find_author che avevamo definito in HomeController proprio a questo scopo, e che cambieremo in questo modo:

def find_author
   return Author.find_by_name(session['login'])
end

A questo punto però si pone il problema di far sì che un utente non possa inviare dei messaggi né creare dei nuovi topic se non dopo aver eseguito il login. In realtà quello di cui abbiamo bisogno è un modo di dire a Rails che prima di poter accedere ad alcune azioni l'utente deve essere autenticato.

Gli autori di Rails però hanno affrontato questo problema in un modo che rende il tutto assolutamente evidente, grazie al meccanismo dei filtri. Un filtro non è altro che una funzione che viene associata ad una o più azioni, e che permette di effettuare delle operazioni prima che essa venga richiamata (come nel nostro caso, per costringere un utente ad autenticarsi) o dopo, per operare sul risultato (applicare una trasformazione XSLT, comprimere la pagina o altro). Per associare un filtro a delle azioni o controller si usa un metodo a livello di classe nel controller, come scaffold, e gli si indica di fare riferimento ad un metodo del controller stesso, quindi nel nostro caso si può aggiungere all'interno della classe HomeController (in app/controllers/home_controller.rb) una cosa del genere:

before_filter :authorize, :only=>[:add_topic,:add_message]
def authorize
   if not @session['login']
     redirect_to(:controller =>'user', :action=>'login') 
   end
end

la sintassi usata per la linea di before_filter potrebbe sembrarvi strana, ma in realtà è molto semplice. Per default, un filtro viene associato a tutti gli elementi di un controller, e questo è il comportamento che si ottiene scrivendo 

before_filter :nome_metodo

Alternativamente, possiamo specificare una lista delle azioni sulle quali deve lavorare un filtro passando come argomento una coppia composta dalla chiave :only e da un array che contiene i nomi dei metodi. Al contrario, se vogliamo applicare  il filtro a tutte le azioni definite nel controller, tranne che ad alcune, possiamo utilizzare un argomento nel quale la chiave sia chiamata :except, ed il valore sia nuovamente un array contenente i nomi delle azioni da ignorare. 

Il contenuto del metodo authenticate è molto semplice: se l'utente ha eseguito il login, nella sessione sarà memorizzato il suo nome relativamente alla chiave "login", e non accadrà nulla. Se invece l'utente non ha eseguito il login, la sessione non avrà niente associato alla chiave, e quindi il valore sarà nil, che come sempre in Ruby è considerato un valore falso, e quindi l'utente verrà rediretto alla pagina di login del controller User.

A questo punto, dovrebbe essere ovvio anche come realizzare un meccanismo di logout, definendo un metodo come questo in UserController

def logout
   session['login']=nil
   redirect_to(:controller=>'home')
end

che semplicemente azzera la variabile di sessione e fa tornare l'utente alla home. Notate che questa azione non ha bisogno di alcuna vista, e che è raggiungibile tramite una semplice GET, ovvero tramite un link. Far sì che questo link sia raggiungibile in tutta l'applicazione richiede solo l'aggiunta di qualche una riga al layout application.rhtml:

<% if session['login'] %>
  <div>
    <p>Effettua il <%=link_to("Logout", :controller=>"user", :action=>"logout") %></p>
  </div>
<% end %>

Abbiamo fatto uso di una semplice condizione if per far si che questo link sia mostrato solo agli autori effettivamente autenticati, e potete vedere come una vista abbia accesso allo stesso oggetto session visibile nei controller, il che ci permette di semplificare il codice.

Attenzione: in teoria un link (ovvero una chiamata al webserver tramite GET) non dovrebbe mai cambiare lo stato, in quanto può capitare che esso sia seguito da un crawler o da un qualunque programma automatizzato. In questo caso abbiamo usato un link per il logout in quanto è l'approccio più comune e non è in generale "dannoso", ma nel realizzare siti senza login fate attenzione ad usare azioni pericolose raggiungibili tramite link, come ad esempio collegamenti che permettano di cancellare degli oggetti.

Prima di proseguire, possiamo notare come ci sia una piccola incongruenza tra l'invio di messaggi e la creazione di nuovi topic. Nel primo caso, il form è visibile fin da subito, anche se un autore non ha ancora eseguito il login, il che potrebbe portarlo ad inserire dei dati senza poi poterli inviare. Possiamo facilmente modificare la vista usando di nuovo di un blocco condizionale, facendo sì che agli utenti autenticati sia mostrato il form per inviare messaggi e agli utenti sconosciuti sia fornito il link per eseguire il login. La vista avrà dunque un pezzo di codice simile a questo:

<% if session['login'] %>
<h3>Aggiungi un nuovo messaggio</h3>

<form action="/home/add_message" method="post">
  Testo: <%= text_area_tag "body" %>
  <%= hidden_field_tag "topic_id", @topic.id %>
  <br />
  <%= submit_tag("Aggiungi messaggio") %>
</form>

<% else %>
<%= link_to("Esegui il login per inviare messaggi", :controller=>'user', :action=>'login') %>
<% end %>

Ti consigliamo anche