Skip to topic | Skip to bottom
Home

TechWeb11
TechWeb11.PutThroughAjaxTutorialr1.20 - 06 May 2011 - 12:50 - FedericoPareschitopic end

Start of topic | Skip to actions
Pagina in costruzione.


0. Introduzione

AUTORE: Federico Pareschi (Morg)

Ciao a tutti ragazzi, l'altro giorno alla riunione PAM mi sono accorto che davvero poca gente fra i presenti non aveva ben chiaro l'utilizzo delle interazioni fra Client-Server per il progetto SPAM e di come dovessero essere gestite (io per primo mi metto e rimango in questa lista di ignari). Appena tornato a casa mi son messo subito a riguardare i vari tipi di metodi per eseguire richieste ed ottenere risposte tra Client e Server (GET,POST,PUT,DELETE,etc) e, rovistando su internet, la maggior parte delle spiegazioni a riguardo parla solo -o principalmente- di GET e POST, menzionando raramente PUT. Andando più a fondo nella fonte infinita di conoscenza che è Google, ho trovato vari esempi sparsi e discontinui su come gestire le richieste PUT su server, principalmente PHP, e ho cercato di mettere insieme una piccola guida a riguardo per aiutare tutti quanti a capire un po' meglio (con esempi pratici funzionanti) come implementare queste interazioni all'interno del proprio progetto.

Ci tengo a sottolineare ed evidenziare il fatto che questo tutorial è sicuramente impreciso e tendenzialmente errato, solo per il fatto che anche io, come voi, non sono assolutamente in grado di garantire la correttezza di questo codice. Sì funziona (spesso), però non è sicuramente agli standard utilizzati dal nostro protocollo (sia PAM che UPIM) e dovrà essere rielaborato da ogni singolo gruppo/progetto in fase di implementazione.

Ogni esempio in questa guida è preso da una breve applicazione Client-Server funzionante fatta da me su un sito esterno (vedi sezione 6) e codificata purtroppo in PHP (il lato Server). Ho scelto di usare PHP solo per il fatto che è il linguaggio più diffuso fra free webserver provider come quello usato da me, per il progetto però la teoria si applica facilmente anche a Ruby, Perl e Python. Se avete aggiunte, commenti o migliorie da fare (se magari volete implementare gli esempi con altri linguaggi), sentitevi liberi di modificare questa pagina come ritenete opportuno, vi chiedo soltanto di lasciare un elenco delle modifiche apportate in fondo alla pagina per una più facile lettura.

Enjoy.

NOTA: Tutti gli esempi che faccio all'interno dell'intera guida hanno gli script in PHP, RIPETO ancora una volta che è indifferente, potrebbero essere in ruby, python e perl e il discorso non cambierebbe.

1. Metodo PUT

In cosa consiste il metodo PUT? Il metodo PUT, come definito da specifica HTTP/1.1 (vedi link esterni), richiede che il messaggio (oggetto) passato dal client al server venga salvato seguendo il percorso presente all'interno dell'URL di richiesta.

In soldoni, questo è quanto, è l'unica cosa di cui abbiamo bisogno per sapere cosa dobbiamo fare quando riceviamo una chiamata con PUT. In sostanza, però, siamo al punto di partenza, che cosa vuol dire tutto questo? Cos'è l'oggetto? Cos'è l'URL di richiesta?

Per oggetto si intende appunto il messaggio, la stringa di dati, inviati dal client al server. Quello che, in pratica, vogliamo salvare sul nostro server. Se andate a vedere le specifiche di SPAM, sotto il protocollo PAM, nelle sezioni 2.1/2.2/2.3 (invio, retweet e risposta di un post), il corpo del messaggio che vogliamo salvare è tutto ciò che parte da article... e chiude con /article . Ci sono vari metodi per gestire tale richiesta e invio di tale messaggio che verranno spiegati più in dettaglio, con esempi, andando avanti nella guida.

Per URL di richiesta si intende proprio la risorsa a cui noi vogliamo accedere (o in questo caso salvare) quando facciamo la richiesta (PUT) al server. Il nostro server riceverà una stringa di connessione esterna da un client che gli chiederà di connettersi a questa risorsa, questa richiesta (che è appunto la PUT) verrà poi elaborata a dovere dal server stesso. L'URL, nel caso di PUT, non richiederà una risorsa in particolare, anzi! Sarà l'URL stesso che dovrà fornire al server un percorso dove salvare la risora che stiamo inviandogli (il messaggio) e il server dovrà elaborare questo URL e decidere dove salvare la risorsa.

Per semplificare: faremo una richiesta a "nomeserver.com/salva/messaggio" con "ciao mamma" come messaggio? Bene, il nostro server allora riceverà come URL /salva/messaggio e come risorsa "ciao mamma". A seconda dello script che avremo settato nel nostro server (vedi sezione 2), saremo noi a gestire questo URL nel modo in cui vogliamo. Possiamo scegliere, per esempio, di interpretare l'URL come percorso all'interno di un albero che ha come radice "salva" e poi scende nelle foglie con "messaggio" e infine salva dentro la foglia "messaggio" il nostro "ciao mamma". In questo modo avremo un percorso (URL) all'interno del server che ci va ad identificare una precisa risorsa ("ciao mamma") nell'albero.

Interessante, ma cosa c'è di diverso da usare POST? La differenza sostanziale fra POST e PUT è che POST è molto più potente e versatile di PUT. Con POST posso fare tutto quello che fa PUT e anche di più! Mi sembra giusto (e molto sensato) voler adottare un metodo POST al posto di PUT, perchè complicarsi la vita con due cose che fanno la stessa cosa e una delle due la fa meglio?

Ci sono vari motivi, meglio prima fornire però un background sul metodo POST e guardare bene come funziona. POST è molto simile a PUT, differisce soltanto per il fatto che POST compie una richiesta (di qualunque tipo, mentre PUT è solitamente un salvataggio di una risorsa) tramite il proprio messaggio, non tramite l'URL. In questo caso, l'URL identifica la risorsa sul server, solitamente un modulo/programma, che dovrà gestire la richiesta e analizzare i dati passati all'interno del messaggio.

Per tornare all'esempio di prima del "ciao mamma", se volessi salvare su un server la scritta "ciao mamma" al percorso salva->messaggio, dovrei inviare una richiesta POST all'url "nomeserver.com/post_save.php" (i.e. modulo che gestisce la mia richiesta di salvataggio risorse) con corpo del messaggio qualcosa di simile a {data1="salva",data2="messaggio",data3="ciao mamma"}. In questo caso il nostro programma post_save.php dovrà interpretare le variabili/dati inviatigli dal client e trovare che data1 e data2 sono il percorso (pezzi del percorso) da seguire all'interno dell'albero e data3 il messaggio vero e proprio da salvare.

Questo era un esempio di POST che svolge le stesse azioni di PUT. Tuttavia, POST è molto di più. Noi possiamo creare moduli all'interno del nostro server per gestire diverse richieste di POST e smettere di preoccuparci del percorso della nostra risorsa (/salva/messaggio), possiamo scrivere un modulo chiamato save.php che ogni volta che riceve un dato lo va ad inserire (aggiungere) all'interno dell'albero, un modulo chiamato edit.php che ogni volta che riceve un dato e un identificatore lo va a cercare (tramite identificatore) all'interno dell'albero e lo modifica, e tanto altro ancora. Con POST posso aggiungere moduli e poi chiamarli quando voglio da lato Client per fargli gestire le mie richieste.

E' evidente, POST è più versatile e potente di PUT. Mi rifaccio la domanda allora:

Ma PERCHE' è meglio usare PUT? Semplice, PUT è idempotente. Che significa idempotente? Se faccio due, tre, quattro, cinque volte la stessa richiesta PUT con lo stesso URL (solitamente!!! non sempre, ci sono alcuni casi particolari) ottengo uno e un solo risultato. Se mi sbaglio a cliccare e invio un messaggio due volte con POST, finisco per salvare due messaggi in due posti diversi (perchè il mio modulo semplicemente prende le richieste e le elabora mettendole dove vuole lui), con PUT questo non succede perchè sono io stesso che dico al server dove salvare la risorsa (i.e. /salva/messaggio) tramite l'URL, posso fare la stessa richiesta infinite volte e il server continuerà a salvarmela (e sovrascrivermela) esattamente nello stesso posto. Ovviamente poi possiamo applicare noi stessi delle modifiche allo script del server e fargli fare cose diverse con la stessa PUT, però l'idea generale è appunto questa.

Tornando all'esempio, con POST abbiamo bisogno di save.php per creare una risorsa in un certo luogo e edit.php (con tanto di parametro ID per identificare la risorsa) per modificarla. Con PUT ci basta dire al server l'URL della risorsa e lui la crea o la sovrascrive (modifica) senza problemi.

Proprio per il fatto che POST non è idempotente bisogna sempre stare molto attenti, è il motivo per il quale i nostri browser quando facciamo refresh (F5) in seguito ad una richiesta POST ci chiedono se vogliamo re-inviare i dati. Perchè quella stessa richiesta che abbiamo GIA' fatto, potrebbe avere risultati diversi. Con PUT il problema, generalmente, non si pone.

2. Impostazioni del Server

Per poter abilitare il nostro server a gestire le richieste PUT, dobbiamo stabilire (generalmente) un modulo/script per fare parsing su ogni richiesta e rispondere nel modo corretto. Analogamente ai singoli moduli delle richieste POST (dove specifichiamo quale script stiamo chiamando e da cui ci aspettiamo una risposta, i.e. edit.php) anche PUT avrà bisogno di un gestore di richieste, solitamente accoppiato con un validatore/autenticatore per evitare che utenti (anche anonimi) senza privilegi possano salvare risorse a caso sul nostro server.

Per fare ciò, dobbiamo trovare un modo per dire al nostro server: "Guarda, tutte le richieste PUT con questo tipo di URL, mandale a questo gestore, queste altre a quest'altro etc etc". Premetto ancora una volta che io sto usando PHP ma che ci sono analoghi anche per Perl,Python e Ruby.

Con PHP questa cosa si fa utilizzando un file .htaccess (sì, questo è il nome del file) che ci aiuta a dare ad Apache (il server su cui gira PHP) alcune direttive con cui lavorare. All'interno di queste direttive possiamo anche dirgli di gestire le richieste PUT (e non solo, anche altri tipi di richieste, ma ora non ci interessano) in modi particolari.

Adesso arriva la parte delicata, mi tocca ammettere (ancora una volta) la mia ignoranza a riguardo e limitarmi a spiegare in maniera grossolana come funziona il file .htaccess. Confesso che ho spulciato risorse e siti su internet e mi son limitato a trovare una copia funzionante di ciò di cui avevo bisogno. Se tale file crea problemi, mi dispiace tantissimo ma non sono in grado di approfondire ulteriormente la cosa. Se volete siete liberi di espandere questo paragrafo.

In ogni caso, il file .htaccess fornisce direttive ad Apache su come comportarsi nel caso di richieste che giungono all'interno della cartella in cui viene salvato. .htaccess ha capacità ricorsiva su tutto l'albero delle sottocartelle all'interno della sua cartella, in questo modo posso salvare un file .htaccess nella root del mio server e assicurarmi che abbia effetto su tutte le cartelle di tutto il server. Inoltre ogni singola sottocartella può avere il proprio .htaccess che, in caso di conflitti interni con l'.htaccess più "esterno", ha valenza superiore e sovrascrive le direttive.

In soldoni ciò che interessa a noi è il seguente file:

RewriteEngine On
RewriteBase /
RewriteRule ^/?(api)/? script.php [NC]
RewriteCond %{REQUEST_METHOD} (PUT|DELETE)
RewriteRule .* script.php

Se avete un server Apache per PHP, aggiungete quelle righe al vostro .htaccess, se non avete un .htaccess createvi un semplice file di testo e mettelo nella cartella di root.

NOTA: "RewriteBase /" identifica la cartella su cui applicare il reindirizzamento della risorsa, se volete lavorare sulla root del vostro server mettete "/", se per sempio volete gestire in maniera diversa tutti i PUT all'interno di "servername.com/altriput/" allora scrivete "RewriteBase /altriput". script.php è il nome del modulo a cui viene reindirizzata la richiesta PUT, il percorso è relativo alla directory di RewriteBase? (NON ROOT DEL SERVER).

Dopo aver fatto tutto ciò (non mi aspetto che lo capiate, io sinceramente non ho idea di come funzioni VERAMENTE), il vostro server Apache dovrebbe essere in grado di smistare richieste PUT negli appositi moduli php. In caso contrario vi consiglio una ricerca più approfondita su Google, perchè la mia conoscenza finisce qui.

3. Ruolo del Client

Quale compito svolge il Client in tutto questo? Una domanda lecita, sin'ora ho parlato principalmente di server-side e di come gestire le richieste senza neppure mai una volta spiegare come fare queste richieste a lato client. Per una spiegazione più dettagliata vi consiglio di passare al prossimo paragrafo, perchè prima è giusto parlare del ruolo del client in tutta questa storia.

Cosa deve fare il mio client? Attraverso il client io devo garantire una corretta richiesta, elaborata in maniera sensata e compatibile con il mio server, ed eventualmente inoltrarla (in seguito ai dovuto controlli) al server tramite URL corretto.

In cosa consiste tutto ciò? Generalmente tramite il client voglio controllare che sia l'URL della richiesta (fondamentale per identificare una risorsa tramite PUT) e il messaggio siano pronti per essere spediti al server. La formattazione in questo caso è molto importante, il server ha bisogno di sapere in che formato deve aspettarsi il dato (i.e. 'text/html') e che risposta ci si aspetta (eventualmente) dal server.

Grazie al redirect di .htaccess, il mio client non deve preoccuparsi di rivolgersi ad uno specifico modulo sul server, a lui non interessa. L'unica cosa fondamentale è che l'URL della mia risorsa sia noto prima dell'invio dei dati. Ci penserà poi il server a chiamare l'apposito script su quell'URL, a noi non interessa.

Nel caso di richieste un po' più avanzate, potrei voler monitorare lo stato del mio PUT mentre il mio messaggio viene elaborato a lato server, in questo caso potrei aggiungere delle funzioni di callback alla mia richiesta (vedi paragrafo 4) che vengono poi chiamate quando la richiesta stessa raggiunge un certo stato(200, 404, 503, etc etc).

Andando più a fondo nella vicenda, è sempre bene fare controlli di accesso a lato client per evitare richieste illecite da parte di esterni al nostro server, porzioni "rotte" di messaggi e richieste non integre possono minare la sicurezza dello script sul server (che in ogni caso dovrebbe fare sempre un doppio controllo sui dati e sui permessi nel caso in cui il client si faccia sfuggire qualcosa).

4. Utilizzo di PUT in Javascript/Ajax

Finalmente siamo al paragrafo più utile di questa guida. Scommetto che avete saltato "Guerra e Pace" (come definito da Wilk stesso) dei paragrafi precedenti e siete subito arrivati qui. Ottimo, mi sta bene. Almeno qui si parla di codice!

Per arrivare subito al nocciolo della questione, mi sento in dovere di dire a tutti quanti che alcuni browser non supportano l'uso del metodo PUT all'interno del campo form in HTML dato che è stata una aggiunta di HTML5. I browser più vecchi sono incompatibili e l'unico modo per usare al meglio PUT è di implementarlo tramite Javascript.

Ci sono tre principali metodi (probabilmente ce ne sono anche altri, però sono tutti molto simili) per svolgere richieste PUT tramite Javascript/Ajax e si suddividono su tre livelli di astrazione: * XmlHttpRequest (XHR) - E' il metodo di base, quello più a basso livello. * Ajax - Implementazione di XHR utilizzando Ajax. * jQuery - Implementazione di Ajax personalizzata, estensione a jQuery.

Partendo dal primo metodo, XHR, che è quello più a basso livello, possiamo capire meglio anche gli altri. XmlHttpRequest? può dare problemi se usate un browser vecchio, leggete qua l'alternativa da applicare per garantire la compatibilità fra browser: w3cSchool.

Cominciamo con gli esempi, le variabili "uri" e "message" contengono rispettivamente l'URL della nostra richiesta e il corpo del messaggio, per facilità di comprensione.

XmlHttpRequest

function sendXMLRaw(uri,message)
{
   var url = "http://servername.com/replyTo/"+uri;
   var xmlHttp = new XMLHttpRequest();
   xmlHttp.open('PUT',url);
   xmlHttp.onreadystatechange = handlerRaw;
   xmlHttp.setRequestHeader('Content-Type','text/html');
   xmlHttp.send(message);  
}

Questo esempio utilizza una funzione che invia una richiesta "XmlHttpRequest" all'url dato (ossia server+/nomeazione/+uri) tramite metodo PUT. La variabile handlerRaw è una funzione Javascript (definita tra poco) che viene applicata all'attributo "onreadystatechange" della nostra richiesta. Questo attributo chiama la funzione specificata ogni volta che la richiesta cambia stato sul server (readystate, 200,404, etc). E' utile avere una funzione di callback quando vogliamo poter comunicare con l'utente che ha fatto la richiesta se è andata a buon fine oppure no. In caso negativo possiamo stampare un messaggio di errore oppure ripetere la richiesta fino a quando non ritorna un valore corretto (come 200 o 201). Dopo aver settato la funzione handlerRaw, passiamo alla nostra richiesta un header definito da una coppia (chiave,valore), possiamo passare quanti header vogliamo e verranno tutti quanti inviati uno dietro l'altro al server che poi deciderà come trattarli. Infine, dopo aver settato tutti i parametri per la nostra PUT, chiamamo send passandogli il messaggio da inviare al server e... voilà! Tutto fatto, adesso è tutto in mano al nostro script (vedi paragrafo 6) che dovrà elaborare la richiesta e comunicare con il client in caso di successo/errore.

Qui di seguito un esempio di funzione per gestire il cambio di stato (handlerRaw):

function handlerRaw()
{
   if(this.readyState==4) //4 = finished
   {
     alert(this.status); //popup final status, 200 = OK
     alert(this.responseText); //popup message sent back from the server
   }
}

Ajax

function sendAjax(uri, message)
{
       var url = "http://servername.com/replyTo/"+uri;
       jQuery.ajax({
          type: 'PUT',
          url: url,
          success: ajaxSuccess,
          error: errorHandler,
          contentType: 'text/html',
          data: message
       });
}

Questo metodo fa riferimento a una richiesta esclusiva di ajax in jQuery, e' sicuramente il metodo migliore da usare, quello piu' compatibile e stabile. La chiamata jQuery.ajax() riceve come parametro un blocco dati (all'interno delle parentesi graffe) con diversi parametri. Questi parametri servono a jQuery per definire il comportamento della funzione, il metodo della richiesta e un eventuale modo di gestione della risposta dal server.

type: Definisce il tipo di richiesta Ajax, alcuni esempi sono 'PUT','POST','GET'. (attenzione, a secondo del browser/server la distinzione fra maiuscole o minuscole e' importante, per avere il massimo della compatibilita' scrivete sempre il nome della richiesta in maiuscolo).

url: URL sul server a cui si inoltra la richiesta.

success: nome della funzione (mostrata in seguito) che viene chiamata quando la richiesta ritorna un valore positivo (200, 201, etc etc).

error: nome della funzione (mostrata in seguito) che viene chiamata quando la richiesta ritorna un valore di errore (404,503, etc etc).

contentType: Content-Type header della nostra richiesta.

data: Il messaggio all'interno della nostra richiesta.

Ci sono altri parametri che possono essere piu' o meno utili a seconda delle necessita', invito a leggere la documentazione di jQuery per scoprirne di piu'.

function ajaxSuccess(data)
{
    alert("Server success message: "+data);
}

La funzione che viene chiamata in caso di successo riceve come parametro di ingresso il messaggio inviato dal server di risposta (all'interno di data). Per avere un controllo piu' dettagliato, esistono vari overload per questa funzione con parametri diversi (simile a errorHandler sotto), per ulteriori spiegazioni e dimostrazioni consiglio ancora una lettura dettagliata della documentazione di jQuery.

function errorHandler(xhr, textStatus, thrownError)
{
   alert(xhr.status);
   alert(thrownError);
}

La funzione che viene chiamata in caso di errore, molto simile a quella in caso di successo, riceve come parametri diversi valori. Un overload molto semplice si aspetta soltanto un parametro data (come ajaxSuccess) contenente il messaggio di risposta del server, tuttavia quando facciamo la gestione degli errori e' sempre meglio (e' consigliato) analizzare il tipo di errore ricevuto per decidere come trattarlo (in caso di problema di connessione potremmo decidere di re-inviare i dati una seconda volta). Xhr contiene l'oggetto XmlHttpRequest? della connessione risultante con il server, il quale puo' poi venire ispezionato (xhr.status) per controllarne lo stato, textStatus contiene una stringa con la descrizione dell'errore avvenuto sul server e thrownError contiene un oggetto eccezione (opzionale) inviato dal server stesso per gestire l'errore.

jQuery

L'ultimo metodo, quello piu' astratto, e' semplicemente un wrapper (interfaccia) attorno al metodo Ajax appena visto. Dato che jQuery possiede gia' delle funzioni predefinite per fare GET e POST ($.get() e $.post()) senza chiamare direttamente la richiesta Ajax, perche' non crearci noi la nostra $.put() per fare la stessa cosa?

Lasciatemi dire che e' principalmente una pessima idea, il metodo che preferisco io e' Ajax, semplice e personalizzabile al punto giusto. $.put e' piu' limitato per quanto riguarda i parametri e per i puristi di Javascript/jQuery potrebbe anche risultare errato (mancanza di namespace e cose simili). Per correttezza io ve lo spiego comunque, poi decidete voi quale metodo preferite.

/* Extend jQuery with functions for PUT and DELETE requests. */
function _ajax_request(url, data, callback, type, method) {
    if (jQuery.isFunction(data)) {
        callback = data;
        data = {};
    }
    return jQuery.ajax({
        type: method,
        url: url,
        data: data,
        success: callback,
        contentType: type
        });
}

jQuery.extend({
    put: function(url, data, callback, type) {
        return _ajax_request(url, data, callback, type, 'PUT');
    },
    delete_: function(url, data, callback, type) {
        return _ajax_request(url, data, callback, type, 'DELETE');
    }
});

Copiate e incollate queste due funzioni all'interno del file in cui volete usare $.put(). Sono le istruzioni per creare l'estensione jQuery per la richiesta in Ajax, sinceramente le ho copiate da un sito su internet (di cui ho purtroppo perso l'indirizzo) e non ha senso perdersi a spiegarle (in quanto tanto varrebbe usare direttamente Ajax). La cosa fondamentale e' comprendere che si tratta semplicemente di una estensione della funzione che abbiamo visto nel metodo prima (con parametri un po' piu' limitati, assenza di errorHandler).

function sendJQuery(uri, message)
{
       var url = "http://servername.com/replyTo/"+uri;
       $.put(url,message,ajaxSuccess,'text/html');
}

Questo e' quello di cui abbiamo bisogno per chiamare la nostra PUT. A prima vista sembra molto piu' semplice degli altri metodi pero', come ho gia' detto, e' piu' limitato e riduttivo. Ovviamente i parametri che passiamo alla funzione sono gli stessi (generalmente), url e' l'URL di richiesta, message il messaggio, ajaxSuccess e' la stessa funzione (identica) dell'esempio precedente per gestire la richiesta in caso di successo e il quarto parametro e' il tipo di contentType.

5. Ruolo del Server

TODO
Spiegazione dell'interazione ed elaborazione dei dati inviati dal client al server con esempi

6. Codice completo

TODO
Link esterni al codice completo testabile su un proprio server (PHP)

7. Link esterni

TODO
Link e risorse esterne per approfondire

-- FedericoPareschi - 05 May 2011

  • Set ALLOWTOPICVIEW =
  • Set ALLOWTOPICCHANGE =

to top

You are here: TechWeb11 > PutThroughAjaxTutorial

to top

Copyright © Fabio Vitali 2017 Last update of PutThroughAjaxTutorial on 06 May 2011 - 12:50 by FedericoPareschi