Salta al contenuto
Note per Studenti Web service, XML, JSON, SOAP e REST

Web service, XML, JSON, SOAP e REST

In questa pagina 9

Finora HTTP ha trasportato documenti destinati a una persona (pagine HTML). Un'applicazione può però voler chiamare un servizio su un'altra macchina e ricevere dati, non una pagina: il prezzo di un libro, l'elenco degli appelli, l'esito di un pagamento. Questo è un web service.

Definizione (web service). Un sistema software progettato per supportare l'interazione fra macchine attraverso una rete, in cui le richieste e le risposte sono messaggi in un formato standard, di solito trasportati da HTTP.

Servono tre cose: un formato per i dati (XML o JSON), un modo per esprimere le operazioni (i messaggi SOAP, oppure i metodi HTTP applicati a risorse con REST) e un trasporto (HTTP: HTTP 1.1 - connessioni persistenti, Content-Length e chunked transfer encodingHTTP/1.1 (oggi RFC 9110 e 9112) rende la connessione persistente di default (si chiude solo con "Connection: close"), rende obbligatorio l'header Host (virtual hosting) e introduce i nuovi metodi PUT, DELETE, OPTIONS, TRACE, Expect: 100-continue, richieste di intervalli (206) e Transfer-Encoding: chunked; con la connessione persistente il client deve sapere dove finisce ogni risposta: lunghezza del corpo nell'ordine HEAD/1xx/204/304 senza corpo, Transfer-Encoding chunked, Content-Length, altrimenti fino alla chiusura; il chunked divide il corpo in blocchi preceduti dalla lunghezza in esadecimale, termina con un chunk 0 e un trailer facoltativo, e si decodifica contando i byte dichiarati (non cercando CRLF).HTTP 1.1 - connessioni persistenti, Content-Length e chunked transfer encoding →). Questa nota presenta i formati, SOAP e REST e li confronta; un servizio REST completo in C è in Esercizio - Servizio REST con JSON (sul modello della prova pratica).

XML

XML (eXtensible Markup Language, W3C) descrive dati strutturati come un albero di elementi delimitati da marcatori (tag). A differenza di HTML i tag non sono prefissati: li definisce chi progetta il formato.

xml
<?xml version="1.0" encoding="UTF-8"?>
<libro id="42">
  <titolo>Reti di Calcolatori</titolo>
  <autori>
    <autore>Rossi</autore>
    <autore>Bianchi</autore>
  </autori>
  <prezzo valuta="EUR">29.9</prezzo>
  <disponibile>true</disponibile>
</libro>

Le regole perché un documento sia ben formato (well-formed):

  • c'è un solo elemento radice;
  • ogni tag di apertura ha il suo tag di chiusura e gli elementi sono annidati correttamente (<a><b></b></a>, mai <a><b></a></b>); un elemento vuoto si può scrivere <br/>;
  • i nomi distinguono maiuscole e minuscole;
  • i valori degli attributi (id="42") stanno fra virgolette;
  • i caratteri speciali nel testo si scrivono con entità: &lt; (<), &gt; (>), &amp; (&), &quot;, &apos;; per un blocco di testo grezzo c'è <![CDATA[ ... ]]>;
  • la dichiarazione iniziale <?xml version="1.0" encoding="UTF-8"?> è facoltativa ma consigliata;
  • i commenti sono <!-- ... -->.

Gli spazi dei nomi (namespace, xmlns:m="http://example.com/libreria") evitano conflitti fra tag di vocabolari diversi: <m:prezzo> e <p:prezzo> sono elementi distinti. La validazione (il documento rispetta uno schema) si fa con una DTD o, meglio, con XML Schema (XSD), che dichiara elementi, tipi e vincoli: un documento ben formato non è automaticamente valido. L'XML si legge in due modi: DOM (si carica tutto in un albero in memoria) o SAX (eventi mentre si legge, senza tenere tutto); in C si usano librerie come libxml2 o expat.

Si trasmette con Content-Type: application/xml o text/xml (Caching, autenticazione, tipi MIME e URI in HTTPLa cache HTTP riusa le risposte senza contattare il server finche' sono fresche (Cache-Control: max-age, Expires; in mancanza una durata euristica pari a circa il 10% del tempo trascorso da Last-Modified) e, quando sono scadute, le rivalida con una richiesta condizionale (If-None-Match con ETag, If-Modified-Since con Last-Modified) alla quale il server risponde 304 senza corpo; no-store vieta di memorizzare, no-cache obbliga a rivalidare, private esclude le cache condivise. L'autenticazione usa 401 con WWW-Authenticate e la risposta Authorization: Basic (base64 di utente:password, solo sopra TLS) o Digest (hash con nonce); 407 vale per i proxy. Content-Type porta il tipo MIME (tipo/sottotipo; parametri come charset e boundary), Accept e gli altri header Accept-* guidano la negoziazione. Un URI (RFC 3986) e' scheme://userinfo@host:porta/percorso?query#frammento, con percent-encoding %HH per i byte non ammessi e regole per risolvere i riferimenti relativi.Caching, autenticazione, tipi MIME e URI in HTTP →).

JSON

JSON (JavaScript Object Notation, RFC 8259) è un formato di testo più leggero, nato dalla sintassi degli oggetti di JavaScript e oggi usato da quasi tutti i linguaggi. Ha sei tipi di valore:

tipo sintassi esempio
oggetto { "chiave": valore, ... } (chiavi stringhe) {"id": 42}
array [ valore, ... ] ["Rossi", "Bianchi"]
stringa caratteri fra virgolette doppie, con escape "Reti di Calcolatori"
numero decimale (niente zeri iniziali, niente esadecimale, niente NaN) 29.9, -3, 1e3
booleano true o false true
nullo null null

Escape nelle stringhe: \", \\, \/, \b, \f, \n, \r, \t, \uXXXX (carattere Unicode a 4 cifre esadecimali). Un carattere di controllo (codice sotto 0x20) deve essere escapato. Non sono ammessi commenti né virgole finali. Il testo scambiato è UTF-8; il tipo MIME è application/json.

Lo stesso libro in JSON:

json
{"id":42,"titolo":"Reti di Calcolatori","autori":["Rossi","Bianchi"],"prezzo":29.9,"disponibile":true}

XML o JSON

Lo stesso libro in forma compatta occupa 102 byte in JSON contro 174 in XML: il tag di chiusura ripete ogni nome.

XML JSON
dimensione maggiore (tag di apertura e chiusura) minore
tipi di dato solo testo (i tipi si dichiarano con XSD) numeri, booleani, null, array nativi
leggibilità per un programma serve un parser XML, DOM o SAX corrisponde alle strutture dei linguaggi (oggetti, liste)
struttura elementi, attributi, testo misto, namespace, commenti solo oggetti e array
validazione DTD, XSD, XSLT per trasformare JSON Schema
uso tipico SOAP, documenti, configurazioni complesse API REST, scambio di dati nel Web

Nessuno dei due è migliore in assoluto: XML è più ricco (namespace, documenti con testo misto a marcatori), JSON è più semplice e di fatto lo standard per le API del Web.

JSON in C

C non ha un tipo "oggetto" né una libreria standard per JSON. Per leggere e scrivere si usano librerie (cJSON, jansson, json-c) oppure, per oggetti piatti come quelli dell'esercizio, un estrattore minimo che cerca la chiave e legge il valore, con tre cautele: gestire le sequenze di escape nelle stringhe, controllare i limiti del buffer, e fare l'escape dei valori quando si genera l'uscita (virgolette, barre inverse e caratteri di controllo: un titolo Dire "ciao" deve diventare "Dire \"ciao\"", altrimenti il JSON generato è rotto). Un estrattore con strstr non è un vero parser: non valida la struttura e può confondere una chiave con una stringa uguale in un valore.

SOAP

SOAP (Simple Object Access Protocol, W3C; versione 1.2 del 2003) è un protocollo per scambiare messaggi XML fra applicazioni, indipendente dal trasporto ma quasi sempre usato su HTTP. Un messaggio è una busta (Envelope) con un Header facoltativo (informazioni di contorno: autenticazione, transazioni) e un Body (il contenuto: l'operazione richiesta o il risultato).

xml
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
  <soap:Body>
    <m:GetPrezzo xmlns:m="http://example.com/libreria">
      <m:id>42</m:id>
    </m:GetPrezzo>
  </soap:Body>
</soap:Envelope>

Con l'associazione a HTTP la busta viaggia nel corpo di una POST a un solo indirizzo (l'endpoint), qualunque sia l'operazione: l'operazione è dentro il Body, non nell'URI né nel metodo.

http
POST /servizi/libreria HTTP/1.1
Host: example.com
Content-Type: application/soap+xml; charset=utf-8
Content-Length: 259

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope ...>...GetPrezzo id 42...</soap:Envelope>

(In SOAP 1.1 il tipo è text/xml e un header SOAPAction indica l'azione.) La risposta è un'altra busta:

xml
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
  <soap:Body>
    <m:GetPrezzoRisposta xmlns:m="http://example.com/libreria">
      <m:prezzo>29.90</m:prezzo>
    </m:GetPrezzoRisposta>
  </soap:Body>
</soap:Envelope>

Gli errori sono segnalati da un elemento Fault nel Body (con Code, per esempio Sender se la colpa è del client e Receiver se del server, Reason, eventuale Detail); l'associazione HTTP usa lo stato 500 per i Fault del servizio.

Il contratto di un servizio SOAP è un file WSDL (Web Services Description Language, XML) che descrive messaggi, operazioni, tipi (con XSD), l'associazione al protocollo e l'indirizzo: un programma lo legge e genera automaticamente il codice per chiamare il servizio. Grazie al WSDL e ai numerosi standard WS-* (sicurezza, transazioni, affidabilità) SOAP si adatta ad ambienti aziendali formali, a costo di messaggi verbosi, di un parsing XML pesante e di una complessità elevata.

REST

REST (REpresentational State Transfer) è uno stile architetturale, descritto da Roy Fielding nella tesi di dottorato del 2000, che sfrutta direttamente come è fatto il Web: non un protocollo ma un insieme di vincoli. Un servizio che li rispetta si dice RESTful.

I vincoli

vincolo significato
client-server separazione fra interfaccia utente e dati
senza stato (stateless) ogni richiesta contiene tutte le informazioni necessarie; il server non conserva il contesto del client fra una richiesta e l'altra (niente sessione sul server)
cache le risposte dichiarano se sono memorizzabili (Caching, autenticazione, tipi MIME e URI in HTTPLa cache HTTP riusa le risposte senza contattare il server finche' sono fresche (Cache-Control: max-age, Expires; in mancanza una durata euristica pari a circa il 10% del tempo trascorso da Last-Modified) e, quando sono scadute, le rivalida con una richiesta condizionale (If-None-Match con ETag, If-Modified-Since con Last-Modified) alla quale il server risponde 304 senza corpo; no-store vieta di memorizzare, no-cache obbliga a rivalidare, private esclude le cache condivise. L'autenticazione usa 401 con WWW-Authenticate e la risposta Authorization: Basic (base64 di utente:password, solo sopra TLS) o Digest (hash con nonce); 407 vale per i proxy. Content-Type porta il tipo MIME (tipo/sottotipo; parametri come charset e boundary), Accept e gli altri header Accept-* guidano la negoziazione. Un URI (RFC 3986) e' scheme://userinfo@host:porta/percorso?query#frammento, con percent-encoding %HH per i byte non ammessi e regole per risolvere i riferimenti relativi.Caching, autenticazione, tipi MIME e URI in HTTP →)
interfaccia uniforme risorse identificate da URI; manipolate tramite rappresentazioni; messaggi autodescrittivi (metodo, codice, Content-Type); collegamenti ipertestuali (HATEOAS)
sistema a livelli fra client e server possono esserci proxy, gateway, cache, bilanciatori (DNS, proxy web, HTTP CONNECT e gateway applicativiUn programma risolve i nomi con getaddrinfo (file hosts e poi DNS); un messaggio DNS (RFC 1035) ha un header di 12 byte (ID, flag QR/RD/RA/TC e RCODE, quattro contatori), una domanda (QNAME a etichette con lunghezza, QTYPE, QCLASS) e record di risposta con nomi eventualmente compressi da puntatori, su UDP porta 53 con ripiego su TCP se il bit TC e' acceso; un web proxy e' un intermediario scelto dal client che riceve richieste con URI assoluto, le inoltra al server (togliendo gli header hop-by-hop, aggiungendo Via e X-Forwarded-For) e puo' memorizzare le risposte; le cache si organizzano in gerarchie e con funzioni hash coerenti, le CDN le replicano vicino agli utenti; il metodo CONNECT chiede al proxy un tunnel TCP verso host:porta e dopo la risposta 2xx il proxy inoltra i byte in entrambe le direzioni senza interpretarli (HTTPS, con restrizione delle porte); un gateway o reverse proxy e' un intermediario scelto dal server che traduce o inoltra le richieste ad altri sistemi (bilanciamento, terminazione TLS, FastCGI, API gateway).DNS, proxy web, HTTP CONNECT e gateway applicativi →)
codice su richiesta (facoltativo) il server può inviare codice eseguito dal client (JavaScript)

Risorse e rappresentazioni

Il concetto centrale è la risorsa: qualunque cosa abbia un nome (un libro, un utente, un ordine, un elenco), identificata da un URI. Il client non vede la risorsa ma una sua rappresentazione (JSON, XML, HTML) scelta con la negoziazione (Accept, Content-Type). Le operazioni sono i metodi HTTP, con il loro significato standard:

operazione metodo e URI successo corpo della richiesta note
elencare GET /libri 200 no sicuro, idempotente
leggere GET /libri/42 200 no 404 se non esiste
creare POST /libri 201 Created con Location: /libri/43 rappresentazione della nuova risorsa il server sceglie l'identificatore; non idempotente
sostituire PUT /libri/42 200 o 204 rappresentazione completa idempotente
modificare in parte PATCH /libri/42 200 solo i campi da cambiare
eliminare DELETE /libri/42 204 No Content no idempotente (la seconda può dare 404)

Gli URI indicano nomi, non verbi: /libri/42 e non /leggiLibro?id=42. Le collezioni si filtrano con la query: GET /libri?autore=Rossi&pagina=2. I codici di stato sono parte del contratto: 200 successo, 201 creato, 204 successo senza corpo, 400 richiesta malformata, 401 autenticazione richiesta, 403 vietato, 404 risorsa inesistente, 405 metodo non permesso (con Allow), 409 conflitto (la risorsa esiste già, versione incoerente), 415 tipo di contenuto non supportato, 500 errore del server.

Esempio. Una sessione completa con il servizio dell'esercizio (le risposte sono quelle reali del programma):

http
POST /tasks HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: application/json
Content-Length: 29

{"title":"Comprare il latte"}
http
HTTP/1.1 201 Created
Content-Type: application/json
Content-Length: 49
Location: /tasks/1
Connection: close

{"id":1,"title":"Comprare il latte","done":false}
http
GET /tasks/9 HTTP/1.1
Host: 127.0.0.1:8080

HTTP/1.1 404 Not Found
Content-Type: application/json
Content-Length: 31

{"error":"compito inesistente"}
http
PUT /tasks/1 HTTP/1.1
Content-Type: application/json
Content-Length: 29

{"title":"Latte","done":true}

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 36

{"id":1,"title":"Latte","done":true}
http
DELETE /tasks/1 HTTP/1.1

HTTP/1.1 204 No Content

Si osservi: la risposta di creazione ha 201 e l'header Location, la risposta di DELETE non ha corpo né Content-Type, e gli errori hanno un corpo JSON con un messaggio.

Altri aspetti

  • Idempotenza. GET, PUT e DELETE ripetute hanno lo stesso effetto di una sola: un client può ritentare in caso di errore di rete. POST no: un ritentativo può creare due risorse (si usano chiavi di idempotenza se serve).
  • Senza stato non vuol dire senza autenticazione: ogni richiesta porta le proprie credenziali (token Bearer, Authorization), così il server può essere replicato dietro un bilanciatore senza condividere sessioni.
  • HATEOAS. La risposta contiene i collegamenti alle azioni possibili ("links": {"self": "/libri/42", "autori": "/libri/42/autori"}): il client li segue invece di costruire URI a mano. È la parte meno applicata dello stile.
  • Livelli di maturità (Richardson): 0 un solo endpoint e la POST come tunnel (SOAP); 1 risorse con URI distinti; 2 uso corretto dei metodi HTTP e dei codici di stato; 3 collegamenti ipertestuali. La maggior parte delle API "REST" è al livello 2.
  • Versioni. Si indicano nell'URI (/v1/libri) o con la negoziazione; un'API pubblicata non si cambia in modo incompatibile.

Un servizio REST in C

Un server REST è un server HTTP (Esercizio - Server HTTP iterativo con GET, HEAD e codici di errore (sul modello della prova pratica)) con tre elementi in più: l'instradamento (riconoscere /tasks e /tasks/ID e il metodo), la lettura del corpo con Content-Length nelle POST/PUT con controllo di Content-Type e dimensione (411, 413, 415), e la serializzazione JSON con l'escape. È quanto realizza Esercizio - Servizio REST con JSON (sul modello della prova pratica).

REST e SOAP a confronto

SOAP REST
natura protocollo con regole rigide stile architetturale, flessibile
formato solo XML (busta) JSON, XML, HTML, testo...
trasporto HTTP (ma anche altri) HTTP
operazioni nel Body del messaggio, tutte con POST allo stesso URI metodi HTTP su URI diversi
descrizione WSDL OpenAPI (facoltativa)
errori elemento Fault (stato 500) codici di stato HTTP (404, 400...)
cache difficile (sempre POST) naturale per GET
prestazioni messaggi grandi, parsing pesante leggero
uso tipico integrazioni aziendali con standard WS-* API pubbliche, applicazioni web e mobili

Sicurezza e buone pratiche

Esercizi collegati

Errori tipici

  • Dire che JSON è "più veloce" di XML per natura: è più compatto e più semplice da elaborare, non è un protocollo diverso.
  • Non fare l'escape di virgolette, barre inverse e caratteri di controllo quando si genera JSON.
  • Usare GET per operazioni che modificano dati, o POST per tutto (livello 0).
  • Rispondere sempre 200, anche in caso di errore, con l'errore nel corpo: il codice di stato è parte del contratto.
  • Creare con POST senza restituire 201 e Location; eliminare e restituire un corpo con 204.
  • Credere che REST sia un protocollo o che "senza stato" significhi "senza autenticazione".
  • Mettere verbi negli URI (/creaLibro); usare PUT per modifiche parziali (sostituisce l'intera risorsa).
  • Confondere XML ben formato e valido.

Domande d'esame

1. Confrontare SOAP e REST e dire per quali casi si sceglie l'uno o l'altro. Traccia. SOAP: protocollo con messaggi XML (Envelope, Header, Body, Fault), operazioni nel corpo di una POST a un solo endpoint, descrizione WSDL, estensioni WS-*; verboso, adatto a integrazioni aziendali con requisiti formali. REST: stile con risorse e URI, metodi HTTP con il loro significato, codici di stato, rappresentazioni in JSON o XML, senza stato, cache; leggero, adatto ad API pubbliche e applicazioni web e mobili.

2. Una API REST per i libri: scrivere le richieste e gli stati per creare, leggere, sostituire ed eliminare il libro 43. Traccia. POST /libri con il JSON del libro: 201 Created e Location: /libri/43. GET /libri/43: 200 con il JSON, oppure 404. PUT /libri/43 con la rappresentazione completa: 200 (o 204), idempotente. DELETE /libri/43: 204 No Content; una seconda DELETE dà 404, ma lo stato del server è lo stesso.

3. Scrivere in JSON e in XML un libro con titolo, due autori e prezzo; dire quali regole devono rispettare per essere validi. Traccia. JSON: {"titolo":"...","autori":["A","B"],"prezzo":29.9}; chiavi stringhe fra virgolette doppie, array con [ ], numeri senza zeri iniziali, niente virgola finale. XML: elemento radice unico <libro>, tag annidati e chiusi (<autori><autore>A</autore>...</autori>), nomi che distinguono maiuscole e minuscole, attributi fra virgolette, caratteri speciali come &amp;. Per XML "valido" significa inoltre conforme a uno schema (XSD).

Versione ripasso

  • Web service. Servizio software usato da altri programmi via rete, con messaggi in formato standard su HTTP. Servono: formato dei dati (XML, JSON), modo di esprimere le operazioni (SOAP o REST), trasporto (HTTP).
  • XML. Albero di elementi con tag; ben formato: una sola radice, tag annidati e chiusi, nomi case-sensitive, attributi fra virgolette, entità &lt; &gt; &amp; &quot; &apos;, <![CDATA[ ]]>. Namespace (xmlns:m="..."); valido = conforme a DTD/XSD. DOM o SAX; libxml2, expat. Tipo application/xml.
  • JSON (RFC 8259). Valori: oggetto {}, array [], stringa (virgolette doppie, escape \" \\ \/ \b \f \n \r \t \uXXXX), numero, true/false, null. Niente commenti né virgole finali, UTF-8, application/json. Esempio: {"id":42,"titolo":"Reti di Calcolatori","autori":["Rossi","Bianchi"],"prezzo":29.9,"disponibile":true} = 102 byte; in XML 174.
XML JSON
dimensione maggiore minore
tipi solo testo (XSD) numeri, booleani, null, array
struttura elementi, attributi, namespace oggetti e array
uso SOAP, documenti API REST
  • JSON in C. Librerie (cJSON, jansson) o estrattore minimo per oggetti piatti: gestire gli escape, controllare i buffer, fare l'escape in uscita (Dire "ciao" -> "Dire \"ciao\""); strstr non è un parser.
  • SOAP. Messaggi XML: Envelope > Header (facoltativo) + Body; errori nell'elemento Fault (Code Sender/Receiver, Reason, stato HTTP 500). Su HTTP: POST a un solo endpoint, Content-Type: application/soap+xml (SOAP 1.1: text/xml + SOAPAction); l'operazione sta nel Body. WSDL descrive il servizio; standard WS-*. Verboso, parsing pesante, adatto a integrazioni aziendali.
  • REST (Fielding, 2000). Stile architetturale. Vincoli: client-server, senza stato (ogni richiesta autosufficiente), cache, interfaccia uniforme (URI, rappresentazioni, messaggi autodescrittivi, HATEOAS), sistema a livelli, codice su richiesta (facoltativo).
operazione richiesta successo
elencare / leggere GET /libri, GET /libri/42 200 (o 404)
creare POST /libri 201 Created + Location
sostituire PUT /libri/42 (rappresentazione completa) 200/204
modificare in parte PATCH /libri/42 200
eliminare DELETE /libri/42 204 No Content
  • Regole. URI = nomi (/libri/42), filtri con query (?autore=Rossi). Codici: 400, 401, 403, 404, 405 + Allow, 409, 415, 500. Idempotenti: GET, PUT, DELETE; POST no. Maturità di Richardson: 0 POST unico, 1 risorse, 2 metodi e codici, 3 HATEOAS. Versioni nell'URI (/v1/).
  • REST vs SOAP. REST: stile, JSON/XML, metodi HTTP su URI diversi, errori con codici di stato, cache naturale, leggero. SOAP: protocollo, solo XML, tutto in POST, errori Fault, cache difficile, pesante.
  • Sicurezza. HTTPS, convalidare gli ingressi, limitare dimensione e annidamento, disabilitare le entità esterne XML (XXE), non fidarsi di Content-Type.
  • Esempi da saper scrivere.
json
{"id":42,"titolo":"Reti di Calcolatori","autori":["Rossi","Bianchi"],"prezzo":29.9,"disponibile":true}
xml
<libro id="42"><titolo>Reti di Calcolatori</titolo><autori><autore>Rossi</autore><autore>Bianchi</autore></autori>
<prezzo>29.9</prezzo><disponibile>true</disponibile></libro>
xml
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
  <soap:Body><m:GetPrezzo xmlns:m="http://example.com/libreria"><m:id>42</m:id></m:GetPrezzo></soap:Body>
</soap:Envelope>
  • Sessione REST. POST /tasks + {"title":"Comprare il latte"} -> 201 Created, Location: /tasks/1, corpo {"id":1,"title":"Comprare il latte","done":false}; GET /tasks/9 -> 404 + {"error":"compito inesistente"}; PUT /tasks/1 + {"title":"Latte","done":true} -> 200; DELETE /tasks/1 -> 204 senza corpo.

  • Errori tipici: JSON senza escape; GET che modifica; sempre 200; POST senza 201/Location; 204 con corpo; verbi negli URI; PUT per modifiche parziali; REST creduto un protocollo; XML ben formato confuso con valido.

Esercizi su questo argomento

Teoria collegata