20 giugno 2009

Tutorial : Cos' è REST ?

Cos'è REST

REST stà per Representational State Transfer.
Esso si basa su un protocollo di comunicazione stateless, client-server, chacheable e si appoggia sul protocollo HTTP.

REST è uno stile arichitetturale per il disegno di applicazioni di rete. L'idea è quella di usare una comunicazione tra macchine basata su richieste HTTP anzichè utilizzare meccanismi come CORBA, RPC o SOAP.

In molti casi, il World Wide Web, basato sull' HTTP, può essere visto come un' architettura REST-based.
Le applicazioni basate su REST dette anche RESTful usano richieste HTTP per inviare dati (creare e/o aggiornare), leggere dati (eseguire query), e cancellare dari.
Quindi REST usa richieste HTTP per tutti e 4 le operazioni di CRUD (Create/Read/Update/Delete).

REST è un alternativa leggere al meccanismo di RPC (Remote Procedure Calls) e quella dei Web Services (SOAP, WSDL, ..).
Dopo vedremo quanto è semplice utilizzare REST.

Malgrado la sua semplicità, REST è permettamente completo, non c'è nulla che si possa fare tramite un Web Service che non si possa fare mediante un'architettura RESTful.
REST non è uno "STANDARD", e mai lo sarà per la W3C.

REST visto come un Web Service leggero

Come un approccio di programmazione, REST è un alternativa leggera i Web Services e ad RPC.
In maniera molto simile ai Web Services, un servizio REST è :
- Indipendente dalla piattaforma
- Indipendente dal linguaggio di programmazione
- Basato su un protocollo di comunicazione Standard (HTTP)
- Poichè usa la porta 80 (porta di default per traffico HTTP) non ha bisogno che ci siano particolari configurazioni del firewall

Per la sicurezza, i token di username/password sono spesso usati.
Per la crittografia, REST può basarsi su protocolli sicuri come HTTPS

Una cosa che non fà parte di un buon disegno di REST sono i cookies: Nell' acronimo di REST, "ST" stà per "State transfer", e quindi, in un buon design queste operazioni sono auto-contenute, e in ogni richiesta vanno trasferite tutte le informazioni (stato) di cui il server ha bisogno per completare la richiesta.

Quanto è semplice REST
Guardiamo un semplice Web Service come esempio : interrogare un applicazione per gli elenchi telefonici per richiedere i dettagli di un utente. Tutto ciò che abbiamo è l'ID dell'utente.

Usando un Web Service e SOAP, la richiesta assomiglierebbe alla seguente:

<?xml version="1.0"?>
<soap:Envelope
xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">
 <soap:body pb="http://www.acme.com/phonebook">
  <pb:GetUserDetails>
   <pb:UserID>12345</pb:UserID>
  </pb:GetUserDetails>
 </soap:Body>
</soap:Envelope>


I dettagli non sono importanti, è solo un esempio.
L'intero xml verrà inviato usando una richista HTTP POST al server.
Il risultato sarà probabilmente un file XML, ma sarà incluso, come payload, dentro l'envelope della risposta SOAP.

E con Rest?
La query probabilmente assomiglierà alla seguente :

http://www.acme.com/phonebook/UserDetails/12345

Nota che questo non è il body della richiesta -- è solo un URL. Questo URL è inviato al server usando una semplice richiesta GET, e la risposta HTTP è il campo con i dati del risultato. Tale risposta non è inclusa all'interno di nulla, i dati saranno trasmessi in maniera tale da essere subito disponibili per un eventuale utilizzo.

E' semplice vedere perchè i Web Services sono spesso usati con librerie che creano la richiesta SOAP/HTTP e la inviano e quindi parsano la risposta SOAP.
Con REST, una semplice connessione di rete è ciò che ti serve. Puoi testare le API direttamente usando il browser.

Comunque le librerie REST esistono (per semplificare le cose), ne discuteremo dopo.
Da notare come nell'URL il metodo non è chiamato "GetUserDetails" ma semplicemente "UserDetails". E' una convenzione comune usata nel disegno di REST di usare nomi anzichè verbi per denotare semplici risorse.

Simpatica Similutudine
Una similitudine simpatica tra REST e SOAP è quella tra una lettera e una cartolina : con SOAP, devi usare una busta da lettera, con REST è una cartolina. La cartolina è più semplice da gestire. Tralaltro c'è una convenienza per quanto riguarda il fattore banda da utilizzare, in quanto i contenuti sono di gran lunga inferiori.

Richieste REST più complesse
La sezione precedente includeva un esempio semplice per una richiesta REST con un unico parametro.
REST può semplicemente gestire richieste più complesse che includono molti parametri.
In molti casi, si possono semplicemente utilizzare parametri GET nell'URL della richiesta HTTP.

Ad esempio:

http://www.acme.com/phonebook/UserDetails?firstName=John&lastName=Doe

Se si ha la necessità di passare parametri lunghi, o binari, è necessario utilizzare richieste HTTP di tipo POST ed includere i parametri nel body della richiesta POST.

Per regola, le richieste GET vengono utilizzare per query di sola lettura, esse non cambiano lo stato del server e dei suoi dati.
Per la creazione, aggiornamento, e cancellazione si usano richieste POST. (POST può anche essere usato per lettura nel caso in cui vengono inviati parametri più complessi).

In un modo, questa pagina web (così come altre) può essere vista offrendo servizi mediante API rest. E' possibile usare una richiesta GET per leggere i dati e una POST per postare un commento.

Mentre un servizio REST può uesare XML nelle sue risposte (come un modo di organizzare dati strutturati), le richieste REST raramente usano l'XML. Come mostrato prima, in molti casi, i parametri di richiesta sono semplici, e non c'è bisogno di utilizzare XML.

Un vantaggio di usare XML è la "safety" dei tipi. Comunque, in un sistema stateless come REST, devi solo verificare la validità del tuo input, XML o altro.


Risposta REST del server

Una risposta del server in REST è spesso un file XML. Per esempio :
<parts-list>
 <part id="3322">
  <name>ACME Boomerang</name>
  <desc>
   Used by Coyote in <i>Zoom at the Top</i>, 1962
  </desc>
  <price currency="usd" quantity="1">17.32</price>
  <uri>http://www.acme.com/parts/3322</uri>
 </part>
 <part id="783">
  <name>ACME Dehydrated Boulders</name>
  <desc>
   Used by Coyote in <i>Scrambled Aches</i>, 1957
  </desc>
  <price currency="usd" quantity="pack">19.95</price>
 <uri>http://www.acme.com/parts/783</uri>
 </part>
</parts-list>


Comunque, altri formati possono essere anche usati, diversamente dai servizi SOAP, REST non è obbligato ad utilizzare XML.
Possibili formati possono essere CVS (comma-separated values) e JSON (JavaScript Object Notation)

Ciascun formato ha i suoi vantaggi e svantaggi. XML è facilmente espandibile (i client possono ignorare campi sconosciuti) ed è type-safe, CSV è molto compatto e JSON è facilmente parsabile dai client Javascript (ed è facilmente parsabile anche in altri linguaggi).

Un opzione non è accettabile come formato di risposta REST, eccetto in casi molto specifici : HTML, o altri formati non sono facilmente processabili dai client. L'eccezione specifica è, sicuramente, quando il servizio REST è documentato per ritornare un documento human-readable (facilmente leggibile dall'uomo). Comunque guardando l'intero WWW come ritorno di un applicazione RESTful, troviamo che l'HTML è infatti il formato di risposta REST più comunemente usato.

Esempi Rest reali

Potete trovare esempi di API REST utilizzate da portali abbastanza noti:

- Twitter API
- Search API

In rete comunque, ne troverete un' infinità.

REST e Ajax
AJAX è una tecnica popolarissima per lo sviluppo web che permette alle pagine di essere interattive mediante il semplice utilizzo di Javascript.

In AJAX, le richieste sono inviate al server usando oggetti di tipo XMLHttpRequest. La risposta è usata dal Javascript che dinamicamente cambia la pagina corrente.

In molti casi, le applicazioni AJAX seguono i principi del disegno REST. Ciascuna XMLHttpRequest può essere vista come una richiesta ad un servizio REST, inviata tramite il metodo GET.
La risposta è spesso in JSON, un formato popolare di risposta per REST.

Per rendere le vostre applicazioni AJAX veramente RESTful, è necessario seguire gli standard di disegno per REST.
Troverete che molto di questo contribuirà ad un buon design.

Comunque in seguito analizzeremo meglio richieste eseguite mediante javascript e AJAX.

Componenti di un architettura REST

Le componenti chiave di un architettura REST sono le seguenti:

- Risorse, che sono dentificate mediante URL logici. Sia stato che funzionalità sono rappresentati usando risorse.
Gli URL logici implacano che le risorse sono universalmente indirizzabili da altre parti del sistema.
Risorse sono gli elementi chiave di un design RESTful, in opposizione ai "metodi" e "servizi" rispettivamente usati nell'RPC e nelle richieste SOAP dei servizi WEB.
- Un web di risorse, significa che una singola risorsa non dovrebbe essere eccessivamente largo e contenere troppi dettagli.
In qualsiasi momento rilevante, una risorsa dovrebbe contenere i link ad informazioni addizionali, così come in pagine Web.
- Il sistema è client-server, ma sicuramente una componente server può essere una componente client.
- Non ci sono connessioni agli stati, l'interazione è stateless. Ogni nuova richiesta può trasportare le informazioni richieste per poterla completare, e non deve contare sulle interazioni precedenti con lo stesso client.
- Le risorse devono essere cachable quando possibile con una data di scadenza. Il protocollo deve permettere al server di specificare esplicitamente quali risorse possono essere messe in chache e per quanto tempo.
Sin da quando l' HTTP è universalmente usato per il protocollo RET, gli header di controllo della cache dell' HTTP sono usati per questo scopo. I clients devono rispettare le specifiche di cache del server per ogni risorsa.
- I server Proxy possono essere usati come parte dell'architettura, per perfezionare le performance e la scalabilità. Ogni proxy HTTP standard può essere usato.

Da notare che l' applicazione può usare i servizi REST senza essere stats scritta per essere un architettura REST. Ad esempio una macchina non pragrammata per REST può accedere a servizi REST di terze parti.

Linee guida per il design di REST

Alcune semplici linee guida per il design di un architettura REST:

Non usare URL fisici. Un URL fisico punta a qualcosa di fisico, ad esempio un file XML:
"http://www.acme.com/inventory/product003.xml". Un URL logico non implica un file fisico : "http://www.acme.com/inventory/product/003".
Certo, tutto ciò che comunque ha estensione .xml potrebbe essere generato dinamicamente. Ma deve essere "umanamente visibile" che un URL è logico e non fisico.

Le Query non devono ritornare troppi dati. Se necessario, fornire un meccanismo di paging. Per esempio una richiesta di tipo GET che chiede ad esempio una "product list) deve restituire i primi n prodotti con i link al prossimo/precedente.
Pensare sempre al fatto che le risposte REST possono essere di qualsiasi tipo, è necessario essere sicuri che tutto sia ben documentato, e non cambiare il formato di output leggermente.

Ricordarsi, nel caso in cui l'output sia umanamente-leggibile, che i client snon sono utenti umani.
Se l'output è un XML, essere sicuri che il documento faccia riferimento ad uno schema o a un DTD.

ROA vs. SOA, REST vs. SOAP

ROA (REST Oriented Architecture) è solo un nome di fantasia per una SOA (Service Oriented Architecture) che usa servizi REST.

Il principale vantaggio di una SOA basata su SOAP riepetto a ROA è che ci sono tool di supporto più "maturi", comunque questo potrà variare nel tempo.
Un altro vantaggio della SOA include la tipatura Safe delle richieste XML (per le risposte, ROA può anch'essa utilizzare XML se gli sviluppatori lo desiderano).

Il vantaggio principale di ROA è la facilità nell' implementazione, agilità nel progetto, e l' approccio alleggerito nell'approccio alle cose. SOA e SOAP è per gente che sviluppa suite business ed è quello che troverete nel sistemi bancari e finanziari.
Viceversa quando c'è la necessità di qualcosa di più veloce, con buone performance e basso sovraccarico, è spesso più indicato utilizzare REST e la ROA.

Per esempio, quando viene spiegato il perchè si scelga REST anzichè SOAP da yahoo, la gente dice che REST ha poche barriere, è più semplice di SOAP, ed è sufficiente per i servizi che yahoo fornisce. Questo è vero non solo tra REST e SOAP, ma più in generale tra ROA e SOA.

Un altro vantaggio è dato dalla performance : con un supporto alla cache migliore, richieste e risposte più leggere, quindi un parsing delle risposte più leggero, REST permette di ridurre notevolmente il traffico.

Documentare i servizi REST : WSDL e WADL

Il WSDL, è uno standard W3C che definisce un linguaggio per la descrizione di servizi web. Esso è comunemente usato per descrivere i servizi offerti dal server SOAP. Mentre il WSDL è flessibile nell' opzione di bind dei servizi, esso non supporta originariamente operazioni HTTP come GET e POST. Sin dall' inizio invece i servizi REST usano altri verbi HTTP, come PUT e DELETE. Il WSDL è una scelta troppo povera per documentare i servizi REST.

Nella versione 2.0, i WSDL supportano tutti i verbi HTTP ed è ora considerato essere un metodo accettabile per la documentazione dei servizi REST.

La seconda alternativa è il WADL ( Web Application Description Language) adottata da Sun Microsystem.
così come REST, il WADL è leggero, semplice da capire e semplice da scrivere rispetto ai WSDL. In alcuni aspetti non è flessibile come il WSDL ma è sufficiente per ogni servizio REST ed è molto meno complesso.

Ecco un frammento di una specifica WADL, che descrive un servizio di "Item Search" di Amazon :

 <method name="GET" id="ItemSearch">
 <request>
  <param name="Service" style="query"
   fixed="AWSECommerceService"/>
  <param name="Version" style="query" fixed="2005-07-26"/>
  <param name="Operation" style="query" fixed="ItemSearch"/>
  <param name="SubscriptionId" style="query"
   type="xsd:string" required="true"/>
  <param name="SearchIndex" style="query"
   type="aws:SearchIndexType" required="true">
    <option value="Books"/>
    <option value="DVD"/>
    <option value="Music"/>
  </param>
  <param name="Keywords" style="query"
   type="aws:KeywordList" required="true"/>
  <param name="ResponseGroup" style="query"
   type="aws:ResponseGroupType" repeating="true">
    <option value="Small"/>
    <option value="Medium"/>
    <option value="Large"/>
    <option value="Images"/>
  </param>
 </request>
 <response>
  <representation mediaType="text/xml"
   element="aws:ItemSearchResponse"/>
 </response>
</method>


Come puoi vedere, il formato è molto più self-explanatory, ed arricchisce il REST con alcune aggiunte come la tipizzazione safety usando i tipi dell' Xml schema.

L' intero documento è lungo solo 10 righe in più di quello precedente (include i namespace dell' XML, importa le grammatiche degli schema, etc. ) e può essere trovato nelle specifiche WADL.

Usare REST con php

Richieste GET

Moderne versioni di PHP rendono insignificante la richiesta di pagine web : ogni funzione che accede ai file funziona in maniera analoga con gli URL. Così si può usare fopen, file_get_contents, o ogni altro tipo di comando eseguendo una richiesta GET. Ad esempio :

$url = "http://www.acme.com/products/3322";
$response = file_get_contents($url);
echo $response;


Ogni parametro passato a richieste GET deve essere encodato. Si possono encodare stringhe usando la funzione urlencode.

Richieste POST

In forte contrasto alla facilità con cui fare una richiesta GET, non c'è un modo semplice per farlo via POST in php.
E' necessario aprire una connessione con il server e manualmente inviare tutti gli headers HTTP. Qui c'è una semplice funzione che fà tutto il lavoro per ogni metodo HTTP :

function httpRequest($host, $port, $method, $path, $params) {
  // Params are a map from names to values
  $paramStr = "";
  foreach ($params as $name, $val) {
    $paramStr .= $name . "=";
    $paramStr .= urlencode($val);
    $paramStr .= "&";
  }

  // Assign defaults to $method and $port, if needed
  if (empty($method)) {
    $method = 'GET';
  }
  $method = strtoupper($method);
  if (empty($port)) {
    $port = 80; // Default HTTP port
  }

  // Create the connection
  $sock = fsockopen($host, $port);
  if ($method == "GET") {
    $path .= "?" . $data;
  }
  fputs($sock, "$method $path HTTP/1.1\r\n");
  fputs($sock, "Host: $host\r\n");
  fputs($sock, "Content-type: " .
               "application/x-www-form-urlencoded\r\n");
  if ($method == "POST") {
    fputs($sock, "Content-length: " . 
                 strlen($paramStr) . "\r\n");
  }
  fputs($sock, "Connection: close\r\n\r\n");
  if ($method == "POST") {
    fputs($sock, $paramStr);
  }

  // Buffer the result
  $result = "";
  while (!feof($sock)) {
    $result .= fgets($sock,1024);
  }

  fclose($sock);
  return $result;
}


Eseguire una richiesta POST usando questa funzione è semplice come :

$resp = httpRequest("www.acme.com",
    80, "POST", "/userDetails",
    array("firstName" => "John", "lastName" => "Doe"));


Un alternativa a questo approccio è usando il supporto ai CURL di PHP.

Usare REST con Java

Bisognerebbe partire con la spiegazione di come eseguire richieste mediante le classi base come HttpURLConnection, però ritengo sia molto più utile utilizzare le Apache Commons library ed in particolare i packages httpclient.

Richieste GET

String request = "http://api.search.yahoo.com/WebSearchService/V1/webSearch?appid=YahooDemo&query=umbrella&results=10";
HttpClient client = new HttpClient();
GetMethod method = new GetMethod(request);
// Send GET request
int statusCode = client.executeMethod(method);


In questo modo con qualche riga di codice eseguiamo una richiesta GET ed otteniamo lo statusCode del risultato della stessa.
Adesso vediamo come processare la risposta:

InputStream rstream = null;
rstream = method.getResponseBodyAsStream();
BufferedReader br = new BufferedReader(new InputStreamReader(rstream));
String line;
while ((line = br.readLine()) != null) {
 System.out.println(line);
}
br.close();


Naturalmente è possibile usare strumenti per eseguire il marshalling dell' xml...
Per questo stò preparando un tutorial.

Richieste POST

Le richeiste post sono usate dai browser web quando eseguono il submit di dati con le form HTML. Il content type di tipo "multipart/form-data" può essere usato per eseguire il submit di form che contengono files, dati non-ASCII e dati binary.
Per le richieste POST, costruiamo i dati POST separatamente dall' URL.

String request = "http://api.search.yahoo.com/WebSearchService/V1/webSearch";
HttpClient client = new HttpClient();
PostMethod method = new PostMethod(request);
// Add POST parameters
method.addParameter("appid","YahooDemo");
method.addParameter("query","umbrella");
method.addParameter("results","10");
// Send POST request
int statusCode = client.executeMethod(method);
InputStream rstream = null;    
// Get the response body
rstream = method.getResponseBodyAsStream();


Richieste POST con i socket

E' possibile anche eseguire le richieste post utilizzando i socket...Vediamo un esempio :

// Create POST data string
String postdata = "appid" + "=" + URLEncoder.encode("YahooDemo", "UTF-8");
postdata += "&" + "query" + "=" + URLEncoder.encode("umbrella", "UTF-8");
postdata += "&" + "results" + "=" + URLEncoder.encode("10", "UTF-8");
// Create a socket to the host
String hostname = "api.search.yahoo.com";
int port = 80;
InetAddress addr = InetAddress.getByName(hostname);
Socket socket = new Socket(addr, port);
// Send header
String path = "/WebSearchService/V1/webSearch";
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF8"));
bw.write("POST " + path + " HTTP/1.0\r\n");
bw.write("Content-Length: " + postdata.length() + "\r\n");
bw.write("Content-Type: application/x-www-form-urlencoded\r\n");
bw.write("\r\n"); 
// Send POST data string
bw.write(postdata);
bw.flush();


La fonte dell'articolo in inglese potete trovarla al seguente link

4 commenti:

Federico Lombardi ha detto...

guida ottima e completa! complimenti..

Samuele Bagatin ha detto...

Friz sei sempre il migliore :)

Samuele Bagatin ha detto...
Questo commento è stato eliminato da un amministratore del blog.
Anonimo ha detto...

Molto utile. Bravo!