Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Un'implementazione dell'API Web RESTful è un'API Web che usa principi architetturali REST (Representational State Transfer) per ottenere un'interfaccia ad accoppiamento libero e senza stato tra un client e un servizio. Un'API Web RESTful supporta il protocollo HTTP standard per eseguire operazioni sulle risorse e restituire rappresentazioni di risorse che contengono collegamenti hypermedia e codici di stato dell'operazione HTTP.
Un'API Web RESTful deve essere allineata ai principi seguenti:
Indipendenza della piattaforma, il che significa che i client possono chiamare l'API Web indipendentemente dall'implementazione interna. Per ottenere l'indipendenza dalla piattaforma, l'API Web usa HTTP come protocollo standard, fornisce una documentazione chiara e supporta un formato di scambio di dati familiare, ad esempio JSON o XML.
Accoppiamento libero, il che significa che il client e il servizio Web possono evolversi in modo indipendente. Il client non deve conoscere l'implementazione interna del servizio Web e il servizio Web non deve conoscere l'implementazione interna del client. Per ottenere un accoppiamento libero in un'API Web RESTful, usare solo protocolli standard e implementare un meccanismo che consenta al client e al servizio Web di accettare il formato dei dati da scambiare.
Questo articolo descrive le procedure consigliate per la progettazione di API Web RESTful. Vengono inoltre illustrati i modelli di progettazione e le considerazioni comuni per la creazione di API Web facili da comprendere, flessibili e gestibili.
Concetti di progettazione dell'API Web RESTful
Per implementare un'API Web RESTful, è necessario comprendere i concetti seguenti.
Uniform Resource Identifier (URI): Le API REST sono progettate per le risorse, che sono qualsiasi tipo di oggetto, dati o servizio a cui il client può accedere. Ogni risorsa è rappresentata da un URI che identifica in modo univoco tale risorsa. Ad esempio, l'URI per un determinato ordine cliente potrebbe essere:
https://api.contoso.com/orders/1
La rappresentazione delle risorse definisce il modo in cui una risorsa identificata dal relativo URI viene codificata e trasportata tramite il protocollo HTTP in un formato specifico, ad esempio XML o JSON. I client che vogliono recuperare una risorsa specifica devono usare l'URI della risorsa nella richiesta all'API. L'API restituisce una rappresentazione della risorsa dei dati indicati dall'URI. Ad esempio, un client può effettuare una richiesta GET all'identificatore
https://api.contoso.com/orders/1
URI per ricevere il corpo JSON seguente:{"orderId":1,"orderValue":99.9,"productId":1,"quantity":1}
L'interfaccia uniforme è il modo in cui le API RESTful ottengono un accoppiamento libero tra le implementazioni client e di servizio. Per le API REST basate su HTTP, l'interfaccia uniforme include l'uso di verbi HTTP standard per eseguire operazioni come
GET
,PUT
POST
,PATCH
, eDELETE
sulle risorse.Modello di richiesta senza stato: Le API RESTful usano un modello di richiesta senza stato, il che significa che le richieste HTTP sono indipendenti e possono verificarsi in qualsiasi ordine. Per questo motivo, non è possibile mantenere le informazioni sullo stato temporaneo tra le richieste. L'unica posizione in cui vengono archiviate le informazioni si trova nelle risorse stesse e ogni richiesta deve essere un'operazione atomica. Un modello di richiesta senza stato supporta una scalabilità elevata perché non deve mantenere alcuna affinità tra client e server specifici. Tuttavia, il modello senza stato può anche limitare la scalabilità a causa di problemi con la scalabilità dell'archiviazione back-end del servizio Web. Per ulteriori informazioni sulle strategie per scalare un archivio dati, consultare Partizionamento dei dati.
Collegamenti Hypermedia: Le API REST possono essere guidate da collegamenti hypermedia contenuti in ogni rappresentazione di risorsa. Ad esempio, il blocco di codice seguente mostra una rappresentazione JSON di un ordine. Contiene collegamenti per ottenere o aggiornare il cliente associato all'ordine.
{ "orderID":3, "productID":2, "quantity":4, "orderValue":16.60, "links": [ {"rel":"product","href":"https://api.contoso.com/customers/3", "action":"GET" }, {"rel":"product","href":"https://api.contoso.com/customers/3", "action":"PUT" } ] }
Definire gli URI delle risorse DELL'API Web RESTful
Un'API Web RESTful è organizzata in base alle risorse. Per organizzare la progettazione della tua API intorno alle risorse, definisci gli URI delle risorse che corrispondono alle entità aziendali. Quando possibile, gli URI delle risorse devono essere basati su sostantivi (la risorsa) e non su verbi (le operazioni sulla risorsa).
Ad esempio, in un sistema di e-commerce, le entità aziendali principali potrebbero essere clienti e ordini. Per creare un ordine, un client invia le informazioni sull'ordine in una richiesta HTTP POST all'URI della risorsa. La risposta HTTP alla richiesta indica se la creazione dell'ordine ha esito positivo.
L'URI per la creazione della risorsa dell'ordine potrebbe essere simile a:
https://api.contoso.com/orders // Good
Evitare di usare verbi negli URI per rappresentare le operazioni. Ad esempio, l'URI seguente non è consigliato:
https://api.contoso.com/create-order // Avoid
Le entità vengono spesso raggruppate in raccolte come clienti o ordini. Una raccolta è una risorsa separata dagli elementi all'interno della raccolta, pertanto deve avere un proprio URI. Ad esempio, l'URI seguente potrebbe rappresentare la raccolta di ordini:
https://api.contoso.com/orders
Dopo che il client recupera la raccolta, può effettuare una richiesta GET all'URI di ogni elemento. Ad esempio, per ricevere informazioni su un ordine specifico, il client esegue una richiesta HTTP GET sull'URI https://api.contoso.com/orders/1
per ricevere il corpo JSON seguente come rappresentazione della risorsa dei dati dell'ordine interno:
{"orderId":1,"orderValue":99.9,"productId":1,"quantity":1}
Convenzioni di denominazione dell'URI delle risorse
Quando si progetta un'API Web RESTful, è importante usare le convenzioni di denominazione e relazione corrette per le risorse:
Usare i sostantivi per i nomi delle risorse. Usare i sostantivi per rappresentare le risorse. Ad esempio, usare
/orders
anziché/create-order
. I metodi HTTP GET, POST, PUT, PATCH e DELETE implicano già l'azione verbale.Usare nomi plurali per denominare gli URI della raccolta. In generale, consente di usare sostantivi plurali per gli URI che fanno riferimento alle raccolte. È consigliabile organizzare gli URI per raccolte ed elementi in una gerarchia. Ad esempio,
/customers
è il percorso della raccolta del cliente ed/customers/5
è il percorso del cliente con un ID uguale a 5. Questo approccio consente di mantenere intuitiva l'API Web. Inoltre, molti framework API Web possono instradare le richieste in base ai percorsi URI con parametri, in modo da poter definire una route per il percorso/customers/{id}
.Si considerino le relazioni tra diversi tipi di risorse e come esporre queste associazioni. Ad esempio,
/customers/5/orders
potrebbe rappresentare tutti gli ordini per il cliente 5. È anche possibile avvicinarsi alla relazione nell'altra direzione rappresentando l'associazione da un ordine a un cliente. In questo scenario, l'URI potrebbe essere/orders/99/customer
. Tuttavia, l'estensione di questo modello troppo lontano può diventare complessa da implementare. Un approccio migliore consiste nell'includere collegamenti nel corpo del messaggio di risposta HTTP in modo che i client possano accedere facilmente alle risorse correlate. Usare Hypertext come motore dello stato dell'applicazione (HATEOAS) per abilitare la navigazione alle risorse correlate descrive questo meccanismo in modo più dettagliato.Mantenere le relazioni semplici e flessibili. In sistemi più complessi, è possibile che si sia propensi a fornire URI che consentono al client di spostarsi tra diversi livelli di relazioni, ad esempio
/customers/1/orders/99/products
. Tuttavia, questo livello di complessità può essere difficile da gestire ed è inflessibile se le relazioni tra le risorse cambiano in futuro. Provare invece a mantenere gli URI relativamente semplici. Dopo che un'applicazione ha un riferimento a una risorsa, dovrebbe essere possibile usare questo riferimento per trovare gli elementi correlati a tale risorsa. È possibile sostituire la query precedente con l'URI/customers/1/orders
per trovare tutti gli ordini per il cliente 1 e quindi usare/orders/99/products
per trovare i prodotti in questo ordine.Suggerimento
Evitare di richiedere URI di risorse più complessi rispetto a raccolta/elemento/raccolta.
Evitare un numero elevato di risorse di piccole dimensioni. Tutte le richieste Web impongono un carico sul server Web. Maggiore è il numero di richieste, maggiore è il carico. Le API Web che espongono un numero elevato di risorse di piccole dimensioni sono note come API Web chatty. Provare a evitare queste API perché richiedono a un'applicazione client di inviare più richieste per trovare tutti i dati necessari. Prendere invece in considerazione la denormalizzazione dei dati e la combinazione di informazioni correlate in risorse più grandi che possono essere recuperate tramite una singola richiesta. Tuttavia, è comunque necessario bilanciare questo approccio rispetto al sovraccarico del recupero dei dati che il client non ha bisogno. Il recupero di oggetti di grandi dimensioni può aumentare la latenza di una richiesta e comportare costi di larghezza di banda maggiori. Per altre informazioni su questi antipattern delle prestazioni, vedere Chatty I/O e Extraneous Fetching.
Evitare di creare API che rispecchiano la struttura interna di un database. Lo scopo di REST è modellare le entità aziendali e le operazioni che un'applicazione può eseguire su tali entità. Un client non deve essere esposto all'implementazione interna. Ad esempio, se i dati vengono archiviati in un database relazionale, l'API Web non deve esporre ogni tabella come raccolta di risorse. Questo approccio aumenta la superficie di attacco e potrebbe causare perdite di dati. Si consideri invece l'API Web come un'astrazione del database. Se necessario, introdurre un livello di mapping tra il database e l'API Web. Questo livello garantisce che le applicazioni client siano isolate dalle modifiche apportate allo schema del database sottostante.
Suggerimento
Potrebbe non essere possibile eseguire il mapping di tutte le operazioni implementate da un'API Web a una risorsa specifica. È possibile gestire questi scenari non di origine tramite richieste HTTP che richiamano una funzione e restituiscono i risultati come messaggio di risposta HTTP.
Ad esempio, un'API Web che implementa semplici operazioni di calcolatrice, ad esempio l'aggiunta e la sottrazione, può fornire URI che espongono queste operazioni come pseudo risorse e usare la stringa di query per specificare i parametri necessari. Una richiesta GET all'URI /add?operand1=99&operand2=1 restituisce un messaggio di risposta con il corpo contenente il valore 100.
Tuttavia, è consigliabile usare queste forme di URI con moderazione.
Definire i metodi dell'API Web RESTful
I metodi dell'API Web RESTful sono allineati ai metodi di richiesta e ai tipi di supporto definiti dal protocollo HTTP. Questa sezione descrive i metodi di richiesta più comuni e i tipi di supporto usati nelle API Web RESTful.
Metodi di richiesta HTTP
Il protocollo HTTP definisce molti metodi di richiesta che indicano l'azione da eseguire su una risorsa. I metodi più comuni usati nelle API Web RESTful sono GET, POST, PUT, PATCH e DELETE. Ogni metodo corrisponde a un'operazione specifica. Quando si progetta un'API Web RESTful, usare questi metodi in modo coerente con la definizione del protocollo, la risorsa a cui si accede e l'azione eseguita.
È importante ricordare che l'effetto di un metodo di richiesta specifico deve dipendere dal fatto che la risorsa sia una raccolta o un singolo elemento. La tabella seguente include alcune convenzioni usate dalla maggior parte delle implementazioni RESTful.
Importante
La tabella seguente usa un'entità di e-commerce customer
di esempio. Un'API Web non deve implementare tutti i metodi di richiesta. I metodi implementati dipendono dallo scenario specifico.
risorsa | INSERISCI | OTTIENI | PUT | CANCELLARE |
---|---|---|---|---|
/clientela | Creazione di un nuovo cliente | Recuperare tutti i clienti | Aggiornamento massivo dei clienti | Rimuovere tutti i clienti |
/customers/1 | Errore | Recuperare i dettagli per il cliente 1 | Aggiornare i dettagli del cliente 1, se esistente | Rimuovere il cliente 1 |
/clienti/1/ordini | Creare un nuovo ordine per il cliente 1 | Recuperare tutti gli ordini per il cliente 1 | Aggiornamento in blocco degli ordini per il cliente 1 | Rimuovere tutti gli ordini per il cliente 1 |
Richieste GET
Una richiesta GET recupera una rappresentazione della risorsa nell'URI specificato. Il corpo del messaggio di risposta contiene i dettagli della risorsa richiesta.
Una richiesta GET deve restituire uno dei codici di stato HTTP seguenti:
Codice di stato HTTP | Motivo |
---|---|
200 (OK) | Il metodo ha restituito correttamente la risorsa. |
204 (No Content (Nessun contenuto)) | Il corpo della risposta non contiene contenuto, ad esempio quando una richiesta di ricerca non restituisce corrispondenze nella risposta HTTP. |
404 (non trovato) | Impossibile trovare la risorsa richiesta. |
Richieste POST
Una richiesta POST deve creare una risorsa. Il server assegna un URI per la nuova risorsa e restituisce tale URI al client.
Importante
Per le richieste POST, un client non deve tentare di creare il proprio URI. Il client deve inviare la richiesta all'URI della raccolta e il server deve assegnare un URI alla nuova risorsa. Se un client tenta di creare il proprio URI e invia una richiesta POST a un URI specifico, il server restituisce il codice di stato HTTP 400 (RICHIESTA NON VALIDA) per indicare che il metodo non è supportato.
In un modello RESTful, le richieste POST vengono usate per aggiungere una nuova risorsa alla raccolta identificata dall'URI. Tuttavia, una richiesta POST può essere usata anche per inviare dati per l'elaborazione a una risorsa esistente, senza la creazione di una nuova risorsa.
Una richiesta POST deve restituire uno dei codici di stato HTTP seguenti:
Codice di stato HTTP | Motivo |
---|---|
200 (OK) | Il metodo ha eseguito alcune operazioni di elaborazione, ma non crea una nuova risorsa. Il risultato dell'operazione potrebbe essere incluso nel corpo della risposta. |
201 (Creato) | La risorsa è stata creata correttamente. L'URI della nuova risorsa è incluso nell'intestazione Location della risposta. Il corpo della risposta contiene una rappresentazione della risorsa. |
204 (No Content (Nessun contenuto)) | Il corpo della risposta non contiene contenuto. |
400 (Richiesta non valida) | Il client ha inserito dati non validi nella richiesta. Il corpo della risposta può contenere altre informazioni sull'errore o un collegamento a un URI che fornisce altri dettagli. |
405 (metodo non consentito) | Il client ha tentato di effettuare una richiesta POST a un URI che non supporta le richieste POST. |
Richiesta HTTP di tipo PUT
Una richiesta PUT deve aggiornare una risorsa esistente se esistente o, in alcuni casi, creare una nuova risorsa se non esiste. Per effettuare una richiesta PUT:
- Il client specifica l'URI per la risorsa e include un corpo della richiesta che contiene una rappresentazione completa della risorsa.
- Il client effettua la richiesta.
- Se esiste già una risorsa con questo URI, viene sostituita. In caso contrario, viene creata una nuova risorsa, se la route la supporta.
I metodi PUT vengono applicati alle risorse che sono singoli elementi, ad esempio un cliente specifico, anziché le raccolte. Un server potrebbe supportare gli aggiornamenti ma non la creazione tramite PUT. L'eventuale supporto della creazione tramite PUT dipende dal fatto che il client possa assegnare in modo significativo e affidabile un URI a una risorsa prima che esista. In caso contrario, usare POST per creare risorse e fare assegnare l'URI al server. Usare quindi PUT o PATCH per aggiornare l'URI.
Importante
Le richieste PUT devono essere idempotenti, il che significa che l'invio della stessa richiesta più volte comporta sempre la modifica della stessa risorsa con gli stessi valori. Se un client invia nuovamente una richiesta PUT, i risultati devono rimanere invariati. Al contrario, le richieste POST e PATCH non sono sicuramente idempotenti.
Una richiesta PUT deve restituire uno dei codici di stato HTTP seguenti:
Codice di stato HTTP | Motivo |
---|---|
200 (OK) | La risorsa è stata aggiornata correttamente. |
201 (Creato) | La risorsa è stata creata correttamente. Il corpo della risposta potrebbe contenere una rappresentazione della risorsa. |
204 (No Content (Nessun contenuto)) | La risorsa è stata aggiornata correttamente, ma il corpo della risposta non contiene alcun contenuto. |
409 (Conflitto) | Impossibile completare la richiesta a causa di un conflitto con lo stato corrente della risorsa. |
Suggerimento
Prendere in considerazione l'implementazione di operazioni HTTP PUT in blocco in grado di eseguire l'invio in batch di aggiornamenti a più risorse in una raccolta. La richiesta PUT deve specificare l'URI della raccolta. Il corpo della richiesta deve specificare i dettagli delle risorse da modificare. Questo approccio può ridurre la verbosità e migliorare le prestazioni.
Richieste PATCH
Una richiesta PATCH esegue un aggiornamento parziale di una risorsa esistente. Il client specifica l'URI per la risorsa. Il corpo della richiesta specifica un set di modifiche da applicare alla risorsa. Questo metodo può essere più efficiente rispetto all'uso delle richieste PUT perché il client invia solo le modifiche e non l'intera rappresentazione della risorsa. PATCH può anche creare una nuova risorsa specificando un set di aggiornamenti a una risorsa vuota o null se il server supporta questa azione.
Con una richiesta PATCH, il client invia un set di aggiornamenti a una risorsa esistente sotto forma di documento patch. Il server elabora il documento patch per eseguire l'aggiornamento. Il documento patch specifica solo un set di modifiche da applicare anziché descrivere l'intera risorsa. La specifica per il metodo PATCH, RFC 5789, non definisce un formato specifico per i documenti patch. Il formato deve essere dedotto dal tipo di supporto nella richiesta.
JSON è uno dei formati di dati più comuni per le API Web. I due formati principali di patch basati su JSON sono patch JSON e patch di merge JSON.
La patch di unione JSON è più semplice della patch JSON. Il documento patch ha la stessa struttura della risorsa JSON originale, ma include solo il subset di campi che devono essere modificati o aggiunti. È inoltre possibile eliminare un campo specificando null
il valore del campo nel documento patch. Questa specifica indica che la patch di unione non è adatta se la risorsa originale può avere valori Null espliciti.
Si supponga, ad esempio, che la risorsa originale abbia la rappresentazione JSON seguente:
{
"name":"gizmo",
"category":"widgets",
"color":"blue",
"price":10
}
Ecco una possibile patch di merge JSON per questa risorsa:
{
"price":12,
"color":null,
"size":"small"
}
Questa patch di merge indica al server di aggiornare price
, eliminare color
e aggiungere size
. I valori per name
e category
non vengono modificati. Per altre informazioni sulla patch di merge JSON, vedere RFC 7396. Il tipo di media per la patch di merge JSON è application/merge-patch+json
.
La patch di merge non è adatta se la risorsa originale può contenere valori Null espliciti a causa del significato speciale di null
nel documento patch. Il documento patch non specifica anche l'ordine in cui il server deve applicare gli aggiornamenti. L'eventuale importanza di questo ordine dipende dai dati e dal dominio. La patch JSON, definita in RFC 6902, è più flessibile perché specifica le modifiche come sequenza di operazioni da applicare, tra cui aggiungere, rimuovere, sostituire, copiare e testare i valori per convalidare i valori. Il tipo MIME per la patch JSON è application/json-patch+json
.
Una richiesta PATCH deve restituire uno dei codici di stato HTTP seguenti:
Codice di stato HTTP | Motivo |
---|---|
200 (OK) | La risorsa è stata aggiornata correttamente. |
400 (Richiesta non valida) | Documento di patch malformato. |
409 (Conflitto) | Il documento patch è valido, ma le modifiche non possono essere applicate alla risorsa nello stato corrente. |
415 (tipo di media non supportato) | Il formato del documento patch non è supportato. |
Richieste DELETE
Una richiesta DELETE rimuove la risorsa nell'URI specificato. Una richiesta DELETE deve restituire uno dei codici di stato HTTP seguenti:
Codice di stato HTTP | Motivo |
---|---|
204 (NESSUN CONTENUTO) | La risorsa è stata eliminata correttamente. Il processo è stato gestito correttamente e il corpo della risposta non contiene altre informazioni. |
404 (NON TROVATO) | La risorsa non esiste. |
Tipi MIME di risorse
La rappresentazione delle risorse è il modo in cui una risorsa identificata dall'URI viene codificata e trasportata tramite il protocollo HTTP in un formato specifico, ad esempio XML o JSON. I client che vogliono recuperare una risorsa specifica devono usare l'URI nella richiesta all'API. L'API risponde restituendo una rappresentazione di risorsa dei dati indicati dall'URI.
Nel protocollo HTTP i formati di rappresentazione delle risorse vengono specificati usando tipi di media, detti anche tipi MIME. Per i dati nonbinary, la maggior parte delle API Web supporta JSON (tipo di supporto = application/json
) ed eventualmente XML (tipo di supporto = application/xml
).
L'intestazione Content-Type in una richiesta o una risposta specifica il formato di rappresentazione della risorsa. L'esempio seguente illustra una richiesta POST che include dati JSON:
POST https://api.contoso.com/orders
Content-Type: application/json; charset=utf-8
Content-Length: 57
{"Id":1,"Name":"Gizmo","Category":"Widgets","Price":1.99}
Se il server non supporta il tipo di supporto, deve restituire il codice di stato HTTP 415 (tipo di supporto non supportato).
Una richiesta client può includere un'intestazione Accept che contiene un elenco di tipi di supporto accettati dal client dal server nel messaggio di risposta. Per esempio:
GET https://api.contoso.com/orders/2
Accept: application/json, application/xml
Se il server non può corrispondere a nessuno dei tipi di supporti elencati, deve restituire il codice di stato HTTP 406 (non accettabile).
Implementare metodi asincroni
A volte un metodo POST, PUT, PATCH o DELETE potrebbe richiedere un'elaborazione che necessita tempo per essere completata. Se si attende il completamento prima di inviare una risposta al client, potrebbe causare una latenza inaccettabile. In questo scenario è consigliabile rendere asincrono il metodo . Un metodo asincrono deve restituire il codice di stato HTTP 202 (accettato) per indicare che la richiesta è stata accettata per l'elaborazione ma è incompleta.
Esporre un endpoint che restituisce lo stato di una richiesta asincrona affinché il client possa monitorare lo stato eseguendo un'interrogazione periodica dell'endpoint di stato. Includere l'URI dell'endpoint di stato nell'intestazione Location della risposta 202. Per esempio:
HTTP/1.1 202 Accepted
Location: /api/status/12345
Se il client invia una richiesta GET a questo endpoint, la risposta deve contenere lo stato corrente della richiesta. Facoltativamente, può includere un tempo stimato per il completamento o un collegamento per annullare l'operazione.
HTTP/1.1 200 OK
Content-Type: application/json
{
"status":"In progress",
"link": { "rel":"cancel", "method":"delete", "href":"/api/status/12345" }
}
Se l'operazione asincrona crea una nuova risorsa, l'endpoint di stato deve restituire il codice di stato 303 (Vedere altro) al termine dell'operazione. Nella risposta 303 includere un'intestazione Location che fornisce l'URI della nuova risorsa:
HTTP/1.1 303 See Other
Location: /api/orders/12345
Per altre informazioni, vedere Fornire supporto asincrono per le richieste a esecuzione prolungata e Modello di Request-Reply asincrono.
Implementare l'impaginazione e il filtro dei dati
Per ottimizzare il recupero dei dati e ridurre le dimensioni del payload, implementare l'impaginazione dei dati e il filtro basato su query nella progettazione dell'API. Queste tecniche consentono ai client di richiedere solo il subset di dati necessari, che può migliorare le prestazioni e ridurre l'utilizzo della larghezza di banda.
La paginazione divide set di dati di grandi dimensioni in blocchi più piccoli e gestibili. Usare parametri di query come
limit
per specificare il numero di elementi da restituire eoffset
per specificare il punto iniziale. Assicurarsi anche di fornire valori predefiniti significativi perlimit
eoffset
, ad esempiolimit=25
eoffset=0
. Per esempio:GET /orders?limit=25&offset=50
limit
: specifica il numero massimo di elementi da restituire.Suggerimento
Per evitare attacchi Denial of Service, è consigliabile imporre un limite superiore al numero di elementi restituiti. Ad esempio, se il servizio imposta
max-limit=25
e un client richiedelimit=1000
, il servizio può restituire 25 elementi o un errore http BAD-REQUEST, a seconda della documentazione dell'API.offset
: specifica l'indice iniziale per i dati.
Il filtro consente ai client di perfezionare il set di dati applicando condizioni. L'API può consentire al client di passare il filtro nella stringa di query dell'URI:
GET /orders?minCost=100&status=shipped
-
minCost
: filtra gli ordini con un costo minimo di 100. -
status
: filtra gli ordini con uno stato specifico.
-
Prendere in considerazione le procedure consigliate seguenti:
L'ordinamento consente ai client di ordinare i dati usando un
sort
parametro comesort=price
.Importante
L'approccio di ordinamento può avere un effetto negativo sulla memorizzazione nella cache perché i parametri della stringa di query fanno parte dell'identificatore di risorsa che molte implementazioni della cache usano come chiave per memorizzare nella cache i dati.
La selezione dei campi per le proiezioni definite dal client consente ai client di specificare solo i campi necessari usando un
fields
parametro comefields=id,name
. Ad esempio, è possibile usare un parametro di stringa di query che accetta un elenco delimitato da virgole di campi, ad esempio /orders?fields=ProductID,Quantity.
L'API deve convalidare i campi richiesti per assicurarsi che il client sia autorizzato ad accedervi e non esponga campi che normalmente non sono disponibili tramite l'API.
Supportare risposte parziali
Alcune risorse contengono campi binari di grandi dimensioni, ad esempio file o immagini. Per superare i problemi causati da connessioni intermittenti e inaffidabili e per migliorare i tempi di risposta, valutare la possibilità di supportare il recupero parziale di risorse binarie di grandi dimensioni.
Per supportare risposte parziali, l'API Web deve supportare l'intestazione Accept-Ranges per le richieste GET per le risorse di grandi dimensioni. Questa intestazione indica che l'operazione GET supporta le richieste parziali. L'applicazione client può inviare richieste GET che restituiscono un subset di una risorsa, specificato come intervallo di byte.
Prendere in considerazione anche l'implementazione di richieste HTTP HEAD per queste risorse. Una richiesta HEAD è simile a una richiesta GET, ad eccezione del fatto che restituisce solo le intestazioni HTTP che descrivono la risorsa, con un corpo del messaggio vuoto. Un'applicazione client può inviare una richiesta HEAD per determinare se recuperare una risorsa usando richieste GET parziali. Per esempio:
HEAD https://api.contoso.com/products/10?fields=productImage
Ecco un messaggio di risposta di esempio:
HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Type: image/jpeg
Content-Length: 4580
L'intestazione Content-Length fornisce le dimensioni totali della risorsa e l'intestazione Accept-Ranges indica che l'operazione GET corrispondente supporta i risultati parziali. L'applicazione client può usare queste informazioni per recuperare l'immagine in blocchi più piccoli. La prima richiesta recupera i primi 2.500 byte usando l'intestazione Range:
GET https://api.contoso.com/products/10?fields=productImage
Range: bytes=0-2499
Il messaggio di risposta indica che questa risposta è parziale restituendo il codice di stato HTTP 206. L'intestazione Content-Length specifica il numero effettivo di byte restituiti nel corpo del messaggio e non le dimensioni della risorsa. L'intestazione Content-Range indica quale parte della risorsa viene restituita (byte da 0 a 2499 su 4580):
HTTP/1.1 206 Partial Content
Accept-Ranges: bytes
Content-Type: image/jpeg
Content-Length: 2500
Content-Range: bytes 0-2499/4580
[...]
Una richiesta successiva dall'applicazione client può recuperare il resto della risorsa.
Implementare HATEOAS
Uno dei motivi principali per usare REST è la possibilità di esplorare l'intero set di risorse senza conoscere in precedenza lo schema URI. Ogni richiesta HTTP GET deve restituire le informazioni necessarie per trovare le risorse correlate direttamente all'oggetto richiesto tramite collegamenti ipertestuali inclusi nella risposta. La richiesta deve inoltre avere informazioni che descrivono le operazioni disponibili in ognuna di queste risorse. Questo principio è noto come HATEOAS o Hypertext come motore dello stato dell'applicazione. Il sistema è effettivamente una macchina a stati finiti e la risposta a ogni richiesta contiene le informazioni necessarie per passare da uno stato a un altro. Non devono essere necessarie altre informazioni.
Annotazioni
Non esistono standard per utilizzo generico che definiscono come modellare il principio HATEOAS. Gli esempi in questa sezione illustrano una possibile soluzione proprietaria.
Ad esempio, per gestire la relazione tra un ordine e un cliente, la rappresentazione di un ordine può includere collegamenti che identificano le operazioni disponibili per il cliente dell'ordine. Il blocco di codice seguente è una possibile rappresentazione:
{
"orderID":3,
"productID":2,
"quantity":4,
"orderValue":16.60,
"links":[
{
"rel":"customer",
"href":"https://api.contoso.com/customers/3",
"action":"GET",
"types":["text/xml","application/json"]
},
{
"rel":"customer",
"href":"https://api.contoso.com/customers/3",
"action":"PUT",
"types":["application/x-www-form-urlencoded"]
},
{
"rel":"customer",
"href":"https://api.contoso.com/customers/3",
"action":"DELETE",
"types":[]
},
{
"rel":"self",
"href":"https://api.contoso.com/orders/3",
"action":"GET",
"types":["text/xml","application/json"]
},
{
"rel":"self",
"href":"https://api.contoso.com/orders/3",
"action":"PUT",
"types":["application/x-www-form-urlencoded"]
},
{
"rel":"self",
"href":"https://api.contoso.com/orders/3",
"action":"DELETE",
"types":[]
}]
}
In questo esempio la links
matrice ha un set di collegamenti. Ogni collegamento rappresenta un'operazione su un'entità correlata. I dati per ogni collegamento includono la relazione ("cliente"), l'URI (https://api.contoso.com/customers/3
), il metodo HTTP e i tipi MIME supportati. L'applicazione client richiede queste informazioni per richiamare l'operazione.
La links
matrice include anche informazioni di riferimento automatico sulla risorsa recuperata. Questi collegamenti hanno la relazione stessa.
Il set di collegamenti restituiti può cambiare a seconda dello stato della risorsa. L'idea che l'ipertesto sia il motore dello stato dell'applicazione descrive questo scenario.
Implementare il controllo delle versioni
Un'API Web non rimane statica. Man mano che cambiano i requisiti aziendali, vengono aggiunte nuove raccolte di risorse. Man mano che vengono aggiunte nuove risorse, le relazioni tra le risorse potrebbero cambiare e la struttura dei dati nelle risorse potrebbe essere modificata. L'aggiornamento di un'API Web per gestire requisiti nuovi o diversi è un processo semplice, ma è necessario considerare gli effetti che tali modifiche hanno sulle applicazioni client che usano l'API Web. Lo sviluppatore che progetta e implementa un'API Web ha il controllo completo su tale API, ma non ha lo stesso grado di controllo sulle applicazioni client create dalle organizzazioni partner. È importante continuare a supportare le applicazioni client esistenti, consentendo alle nuove applicazioni client di usare nuove funzionalità e risorse.
Un'API Web che implementa il controllo delle versioni può indicare le funzionalità e le risorse esposte e un'applicazione client può inviare richieste indirizzate a una versione specifica di una funzionalità o di una risorsa. Le sezioni seguenti descrivono diversi approcci, ognuno dei quali presenta vantaggi e compromessi.
Nessun controllo delle versioni
Questo approccio è il più semplice e può funzionare per alcune API interne. Le modifiche significative possono essere rappresentate come nuove risorse o nuovi collegamenti. L'aggiunta di contenuto alle risorse esistenti potrebbe non presentare una modifica di rilievo perché le applicazioni client che non prevedono di visualizzare il contenuto lo ignorano.
Ad esempio, una richiesta all'URI https://api.contoso.com/customers/3
deve restituire i dettagli di un singolo cliente che contiene i id
campi , name
e address
previsti dall'applicazione client:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"id":3,"name":"Fabrikam, Inc.","address":"1 Microsoft Way Redmond WA 98053"}
Annotazioni
Per semplicità, le risposte di esempio illustrate in questa sezione non includono collegamenti HATEOAS.
Se il DateCreated
campo viene aggiunto allo schema della risorsa del cliente, la risposta sarà simile alla seguente:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"id":3,"name":"Fabrikam, Inc.","dateCreated":"2025-03-22T12:11:38.0376089Z","address":"1 Microsoft Way Redmond WA 98053"}
Le applicazioni client esistenti potrebbero continuare a funzionare correttamente se possono ignorare i campi non riconosciuti. Nel frattempo, le nuove applicazioni client possono essere progettate per gestire questo nuovo campo. Tuttavia, potrebbero verificarsi modifiche più drastiche allo schema delle risorse, incluse le rimozioni di campi o la ridenominazione. In alternativa, le relazioni tra le risorse potrebbero cambiare. Questi aggiornamenti possono costituire modifiche di rilievo che impediscono il corretto funzionamento delle applicazioni client esistenti. In questi scenari, considerare uno degli approcci seguenti:
- Controllo delle versioni URI
- Controllo delle versioni delle stringhe di query
- Controllo delle versioni delle intestazioni
- Versionamento dei tipi di media
Controllo delle versioni URI
Ogni volta che si modifica l'API Web o si modifica lo schema delle risorse, si aggiunge un numero di versione all'URI per ogni risorsa. Gli URI esistenti in precedenza devono continuare a funzionare normalmente restituendo risorse conformi allo schema originale.
Ad esempio, il address
campo nell'esempio precedente viene ristrutturato in sottocampi che contengono ogni parte costitutiva dell'indirizzo, ad esempio streetAddress
, state
city
, e zipCode
. Questa versione della risorsa può essere esposta tramite un URI che contiene un numero di versione, ad esempio https://api.contoso.com/v2/customers/3
:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"id":3,"name":"Fabrikam, Inc.","dateCreated":"2025-03-22T12:11:38.0376089Z","address":{"streetAddress":"1 Microsoft Way","city":"Redmond","state":"WA","zipCode":98053}}
Questo meccanismo di controllo delle versioni è semplice, ma dipende dal server per instradare la richiesta all'endpoint appropriato. Tuttavia, può diventare difficile man mano che l'API Web matura attraverso diverse iterazioni e il server deve supportare molte versioni diverse. Dal punto di vista di un purista, in tutti i casi, le applicazioni client recuperano gli stessi dati (cliente 3), quindi l'URI non deve differire in base alla versione. Questo schema complica anche l'implementazione di HATEOAS perché tutti i collegamenti devono includere il numero di versione negli URI.
Controllo delle versioni delle stringhe di query
Anziché fornire più URI, è possibile specificare la versione della risorsa usando un parametro all'interno della stringa di query aggiunta alla richiesta HTTP, ad esempio https://api.contoso.com/customers/3?version=2
. Per impostazione predefinita, il parametro version deve essere un valore significativo, ad esempio 1, se le applicazioni client precedenti lo omettono.
Questo approccio presenta il vantaggio semantico che la stessa risorsa viene sempre recuperata dallo stesso URI. Tuttavia, questo metodo dipende dal codice che gestisce la richiesta per analizzare la stringa di query e restituire la risposta HTTP appropriata. Questo approccio complica anche l'implementazione di HATEOAS allo stesso modo del meccanismo di controllo delle versioni dell'URI.
Annotazioni
Alcuni Web browser e proxy Web meno recenti non memorizzano nella cache le risposte per le richieste che includono una stringa di query nell'URI. Le risposte non memorizzate in cache possono ridurre le prestazioni per le applicazioni Web che usano un'API Web ed eseguite da un Web browser meno recente.
Versionamento delle intestazioni
Anziché aggiungere il numero di versione come parametro della stringa di query, è possibile implementare un'intestazione personalizzata che indica la versione della risorsa. Questo approccio richiede che l'applicazione client aggiunga l'intestazione appropriata alle richieste. Tuttavia, il codice che gestisce la richiesta client può usare un valore predefinito, ad esempio la versione 1, se l'intestazione della versione viene omessa.
Negli esempi seguenti viene usata un'intestazione personalizzata denominata Custom-Header. Il valore di questa intestazione indica la versione dell'API Web.
Versione 1:
GET https://api.contoso.com/customers/3
Custom-Header: api-version=1
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"id":3,"name":"Fabrikam, Inc.","address":"1 Microsoft Way Redmond WA 98053"}
Versione 2:
GET https://api.contoso.com/customers/3
Custom-Header: api-version=2
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"id":3,"name":"Fabrikam, Inc.","dateCreated":"2025-03-22T12:11:38.0376089Z","address":{"streetAddress":"1 Microsoft Way","city":"Redmond","state":"WA","zipCode":98053}}
Analogamente al controllo delle versioni degli URI e al controllo delle versioni delle stringhe di query, è necessario includere l'intestazione personalizzata appropriata in tutti i collegamenti per implementare HATEOAS.
Versionamento dei tipi di supporto
Quando un'applicazione client invia una richiesta HTTP GET a un server Web, deve usare un'intestazione Accept per specificare il formato del contenuto che può gestire. In genere, lo scopo dell'intestazione Accept è consentire all'applicazione client di specificare se il corpo della risposta deve essere XML, JSON o un altro formato comune che il client può analizzare. Tuttavia, è possibile definire tipi di supporti personalizzati che includono informazioni che consentono all'applicazione client di indicare la versione di una risorsa prevista.
Nell'esempio seguente viene illustrata una richiesta che specifica un'intestazione Accept con il valore application/vnd.contoso.v1+json
. L'elemento vnd.contoso.v1
indica al server Web che deve restituire la versione 1 della risorsa. L'elemento json
specifica che il formato del corpo della risposta deve essere JSON:
GET https://api.contoso.com/customers/3
Accept: application/vnd.contoso.v1+json
Il codice che gestisce la richiesta è responsabile dell'elaborazione dell'intestazione Accept e di rispettarla il più possibile. L'applicazione client può specificare più formati nell'intestazione Accept, che consente al server Web di scegliere il formato più appropriato per il corpo della risposta. Il server Web conferma il formato dei dati nel corpo della risposta usando l'intestazione Content-Type:
HTTP/1.1 200 OK
Content-Type: application/vnd.contoso.v1+json; charset=utf-8
{"id":3,"name":"Fabrikam, Inc.","address":"1 Microsoft Way Redmond WA 98053"}
Se l'intestazione Accept non specifica alcun tipo di supporto noto, il server Web può generare un messaggio di risposta HTTP 406 (Non accettabile) o restituire un messaggio con un tipo di supporto predefinito.
Questo meccanismo di controllo delle versioni è semplice e adatto per HATEOAS, che può includere il tipo MIME di dati correlati nei collegamenti alle risorse.
Annotazioni
Quando si seleziona una strategia di controllo delle versioni, implicazioni, in particolare in relazione alla memorizzazione nella cache del server Web. Gli schemi di versionamento dell'URI e delle stringhe di query sono ottimizzati per la cache perché ogni volta la stessa combinazione di URI o stringa di query si riferisce agli stessi dati.
I meccanismi di versionamento delle intestazioni e di versionamento dei tipi di media richiedono in genere più logica per esaminare i valori nell'intestazione personalizzata o nell'intestazione Accept. In un ambiente su larga scala, molti client che usano versioni diverse di un'API Web possono comportare una quantità significativa di dati duplicati in una cache lato server. Questo problema può diventare acuto se un'applicazione client comunica con un server Web tramite un proxy che implementa la memorizzazione nella cache e inoltra solo una richiesta al server Web se attualmente non contiene una copia dei dati richiesti nella cache.
API Web multi-tenant
Una soluzione API Web multi-tenant è condivisa da più tenant, ad esempio organizzazioni distinte con gruppi di utenti specifici.
La multi-tenancy influisce in modo significativo sulla progettazione dell'API Web perché determina il modo in cui le risorse sono accessibili e individuate in più tenant all'interno di una singola API Web. Progettare un'API con la multiutenza in mente per aiutare a evitare la necessità di un futuro refactoring per implementare l'isolamento, la scalabilità o personalizzazioni specifiche per i tenant.
Un'API ben progettata deve definire chiaramente il modo in cui i tenant vengono identificati nelle richieste, sia tramite sottodomini, percorsi, intestazioni o token. Questa struttura garantisce un'esperienza coerente e flessibile per tutti gli utenti all'interno del sistema. Per altre informazioni, vedere Eseguire il mapping delle richieste ai tenant in una soluzione multi-tenant.
La multi-tenancy influisce sulla struttura degli endpoint, sulla gestione delle richieste, sull'autenticazione e sull'autorizzazione. Questo approccio influisce anche sul modo in cui i gateway API, i bilanciatori di carico e i servizi back-end instradano ed elaborano le richieste. Le strategie seguenti sono modi comuni per ottenere la multi-tenancy in un'API Web.
Usare il sottodominio o l'isolamento basato su dominio (tenancy a livello DNS)
Questo approccio indirizza le richieste usando domini specifici del tenant. I domini wildcard utilizzano i sottodomini per flessibilità e semplicità. I domini personalizzati, che consentono ai tenant di usare i propri domini, offrono un maggiore controllo e possono essere personalizzati in base alle esigenze specifiche. Entrambi i metodi si basano sulla configurazione DNS appropriata, inclusi i record A
e CNAME
, per indirizzare il traffico all'infrastruttura appropriata. I domini wildcard semplificano la configurazione, ma i domini personalizzati offrono un'esperienza più coerente con il marchio.
Mantenere il nome host tra il proxy inverso e i servizi back-end per evitare problemi come il reindirizzamento url e impedire l'esposizione di URL interni. Questo metodo garantisce il routing corretto del traffico specifico del tenant e consente di proteggere l'infrastruttura interna. La risoluzione DNS è fondamentale per ottenere la residenza dei dati e garantire la conformità alle normative.
GET https://adventureworks.api.contoso.com/orders/3
Trasmettere intestazioni HTTP specifiche del tenant
Le informazioni sul tenant possono essere passate tramite intestazioni HTTP personalizzate come X-Tenant-ID
o X-Organization-ID
tramite intestazioni basate su host come Host
o X-Forwarded-Host
oppure possono essere estratte da attestazioni JWT (JSON Web Token). La scelta dipende dalle funzionalità di routing del gateway API o del proxy inverso, con soluzioni basate su intestazioni che richiedono un gateway di livello 7 (L7) per controllare ogni richiesta. Questo requisito aggiunge un sovraccarico di elaborazione, che aumenta i costi di calcolo quando il traffico scala. Tuttavia, l'isolamento basato su testata offre vantaggi importanti. Consente l'autenticazione centralizzata, semplificando la gestione della sicurezza tra le API multi-tenant. Usando SDK o client API, il contesto del tenant viene gestito dinamicamente in fase di esecuzione, riducendo la complessità della configurazione lato client. Inoltre, mantenere il contesto del tenant nelle intestazioni comporta una progettazione dell'API RESTful più pulita evitando i dati specifici del tenant nell'URI.
Una considerazione importante per il routing basato sulle intestazioni è che complica la memorizzazione nella cache, in particolare quando i livelli della cache si basano esclusivamente sulle chiavi basate su URI e non tengono conto delle intestazioni. Poiché la maggior parte dei meccanismi di memorizzazione nella cache è ottimizzata per le ricerche URI, l'uso delle intestazioni può causare voci di cache frammentate. Le voci frammentate riducono gli hit della cache e aumentano il carico sul back-end. In modo più critico, se un livello di memorizzazione nella cache non differenzia le risposte in base alle intestazioni, può fornire dati memorizzati nella cache destinati a un tenant rispetto a un altro, creando un rischio di perdita di dati.
GET https://api.contoso.com/orders/3
X-Tenant-ID: adventureworks
o
GET https://api.contoso.com/orders/3
Host: adventureworks
o
GET https://api.contoso.com/orders/3
Authorization: Bearer <JWT-token including a tenant-id: adventureworks claim>
Trasmettere informazioni specifiche del tenant attraverso il percorso URI
Questo approccio aggiunge gli identificatori del tenant all'interno della gerarchia di risorse e si basa sul gateway API o sul proxy inverso per determinare il tenant appropriato in base al segmento di percorso. L'isolamento basato sul percorso è efficace, ma compromette la progettazione RESTful dell'API Web e introduce una logica di routing più complessa. Spesso richiede criteri di ricerca o espressioni regolari per analizzare e canonizzare il percorso URI.
Al contrario, l'isolamento basato su header trasmette informazioni sul tenant attraverso le intestazioni HTTP sotto forma di coppie chiave-valore. Entrambi gli approcci consentono una condivisione efficiente dell'infrastruttura per ridurre i costi operativi e migliorare le prestazioni nelle API Web multi-tenant su larga scala.
GET https://api.contoso.com/tenants/adventureworks/orders/3
Abilitare la traccia distribuita e il contesto di traccia nelle API
Man mano che i sistemi distribuiti e le architetture di microservizi diventano gli standard, aumenta la complessità delle architetture moderne. L'uso di intestazioni, ad esempio Correlation-ID
, X-Request-ID
o X-Trace-ID
, per propagare il contesto di traccia nelle richieste API è una procedura consigliata per ottenere visibilità end-to-end. Questo approccio consente di tenere traccia senza problemi delle richieste man mano che passano dal client ai servizi back-end. Semplifica l'identificazione rapida degli errori, monitora la latenza e esegue il mapping delle dipendenze api tra i servizi.
Le API che supportano l'inclusione di informazioni di traccia e contesto migliorano il livello di osservabilità e le funzionalità di debug. Abilitando la traccia distribuita, queste API consentono una comprensione più granulare del comportamento del sistema e semplificano il rilevamento, la diagnosi e la risoluzione dei problemi in ambienti complessi e a più servizi.
GET https://api.contoso.com/orders/3
Correlation-ID: 0f8fad5b-d9cb-469f-a165-70867728950e
HTTP/1.1 200 OK
...
Correlation-ID: 0f8fad5b-d9cb-469f-a165-70867728950e
{...}
Modello di maturità api Web
Nel 2008 Leonard Richardson ha proposto ciò che è ora noto come Richardson Maturity Model (RMM) per le API Web. RMM definisce quattro livelli di maturità per le API Web ed è basato sui principi di REST come approccio architetturale alla progettazione di servizi Web. In RMM, man mano che aumenta il livello di maturità, l'API diventa più RESTful e più strettamente segue i principi di REST.
I livelli sono i seguenti:
- Livello 0: Definire un URI e tutte le operazioni sono richieste POST a questo URI. I servizi Web Simple Object Access Protocol sono in genere a questo livello.
- Livello 1: Creare URI separati per singole risorse. Questo livello non è ancora RESTful, ma inizia a essere allineato alla progettazione RESTful.
- Livello 2: Usare i metodi HTTP per definire le operazioni sulle risorse. In pratica, molte API Web pubblicate sono allineate approssimativamente a questo livello.
- Livello 3: Usare hypermedia (HATEOAS). Questo livello è veramente un'API RESTful, in base alla definizione di Fielding.
Iniziativa OpenAPI
L'iniziativa OpenAPI è stata creata da un consorzio del settore per standardizzare le descrizioni delle API REST tra i fornitori. La specifica di standardizzazione è stata chiamata Swagger prima di essere portata sotto l'iniziativa OpenAPI e rinominata in OpenAPI Specification (OAS).
È possibile adottare OpenAPI per le API Web RESTful. Considerare i punti seguenti:
Il modello OAS include un insieme di linee guida specifiche per la progettazione delle API REST. Le linee guida sono vantaggiose per l'interoperabilità, ma richiedono di assicurarsi che la progettazione sia conforme alle specifiche.
OpenAPI promuove un approccio contract-first anziché un approccio di implementazione-first. Contract-first significa che si progetta prima il contratto API (l'interfaccia) e quindi si scrive codice che implementa il contratto.
Strumenti come Swagger (OpenAPI) possono generare librerie client o documentazione dai contratti API. Per un esempio, vedere la documentazione di ASP.NET Core Web API con Swagger/OpenAPI.
Passaggi successivi
- Vedere consigli dettagliati per la progettazione di API REST in Azure.
- Vedere un elenco di controllo degli elementi da considerare durante la progettazione e l'implementazione di un'API Web.
- Creare architetture di soluzioni software come servizio e multi-tenant in Azure.