Che cos'è uno schema raccomandato per la pianificazione degli endpoint REST per modifiche previsionali


25

Cercare di progettare un'API per applicazioni esterne con lungimiranza per il cambiamento non è facile, ma un po 'di pensiero iniziale può semplificare la vita in seguito. Sto cercando di stabilire uno schema che supporterà le modifiche future pur rimanendo compatibile con le versioni precedenti lasciando in posizione gestori di versioni precedenti.

La preoccupazione principale di questo articolo è quale modello dovrebbe essere seguito per tutti gli endpoint definiti per un determinato prodotto / azienda.

Schema di base

Dato un modello di URL di base, https://rest.product.com/ho ideato che tutti i servizi risiedono /apiinsieme ad /authaltri endpoint non basati su riposo come /doc. Pertanto posso stabilire gli endpoint di base come segue:

https://rest.product.com/api/...
https://rest.product.com/auth/login
https://rest.product.com/auth/logout
https://rest.product.com/doc/...

Endpoint di servizio

Ora per gli stessi endpoint. La preoccupazione POST, GET, DELETEnon è l'obiettivo primario di questo articolo ed è la preoccupazione su quelle azioni stesse.

Gli endpoint possono essere suddivisi in spazi dei nomi e azioni. Ogni azione deve inoltre presentarsi in modo tale da supportare cambiamenti fondamentali nel tipo di ritorno o nei parametri richiesti.

Prendendo un ipotetico servizio di chat in cui gli utenti registrati possono inviare messaggi, potremmo avere i seguenti endpoint:

https://rest.product.com/api/messages/list/{user}
https://rest.product.com/api/messages/send

Ora per aggiungere il supporto della versione per future modifiche alle API che potrebbero non funzionare. Potremmo aggiungere la firma della versione dopo /api/o dopo /messages/. Dato l' sendendpoint, potremmo quindi avere quanto segue per v1.

https://rest.product.com/api/v1/messages/send
https://rest.product.com/api/messages/v1/send

Quindi la mia prima domanda è: qual è un posto consigliato per l'identificatore di versione?

Gestione del codice del controller

Quindi ora abbiamo stabilito che dobbiamo supportare le versioni precedenti, quindi in qualche modo dobbiamo gestire il codice per ciascuna delle nuove versioni che potrebbero svalutarsi nel tempo. Supponendo che stiamo scrivendo endpoint in Java, potremmo gestirlo tramite pacchetti.

package com.product.messages.v1;
public interface MessageController {
    void send();
    Message[] list();
}

Ciò ha il vantaggio che tutto il codice è stato separato attraverso spazi dei nomi in cui qualsiasi modifica errata significherebbe che una nuova copia degli endpoint del servizio. Lo svantaggio è che tutto il codice deve essere copiato e che le correzioni di bug hanno voluto essere applicate a versioni nuove e precedenti che devono essere applicate / testate per ogni copia.

Un altro approccio è quello di creare gestori per ciascun endpoint.

package com.product.messages;
public class MessageServiceImpl {
    public void send(String version) {
        getMessageSender(version).send();
    }
    // Assume we have a List of senders in order of newest to oldest.
    private MessageSender getMessageSender(String version) {
        for (MessageSender s : senders) {
            if (s.supportsVersion(version)) {
                return s;
            }
        }
    }
}

Questo ora isola il controllo delle versioni per ogni endpoint stesso e rende le correzioni dei bug compatibili con la porta posteriore, nella maggior parte dei casi devono essere applicate solo una volta, ma significa che dobbiamo fare un po 'più di lavoro su ogni singolo endpoint per supportare questo.

Quindi c'è la mia seconda domanda "Qual è il modo migliore per progettare il codice del servizio REST per supportare le versioni precedenti."

Risposte:


13

Quindi c'è la mia seconda domanda "Qual è il modo migliore per progettare il codice del servizio REST per supportare le versioni precedenti."

Un'API ortogonale progettata con molta attenzione e probabilmente non dovrà mai essere modificata in modo incompatibile con le versioni precedenti, quindi davvero il modo migliore non è avere versioni future.

Certo, probabilmente non lo capirai davvero al primo tentativo; Così:

  • Versione della tua API, proprio come stai pianificando (ed è l'API che ha la versione, non i singoli metodi all'interno) e fai molto rumore su di essa. Assicurati che i tuoi partner sappiano che l'API può cambiare e che le loro applicazioni dovrebbero verificare se stanno utilizzando l'ultima versione; e consiglia agli utenti di eseguire l'aggiornamento quando è disponibile uno nuovo. Supportare due vecchie versioni è difficile, supportarne cinque è insostenibile.
  • Resistere alla tentazione di aggiornare la versione dell'API con ogni "release". Le nuove funzionalità possono in genere essere implementate nella versione corrente senza interrompere i client esistenti; sono nuove funzionalità. L'ultima cosa che vuoi è che i client ignorino il numero di versione, dato che è comunque principalmente retrocompatibile. Aggiorna la versione dell'API solo quando non puoi assolutamente andare avanti senza interrompere l'API esistente.
  • Quando arriva il momento di creare una nuova versione, il primo client dovrebbe essere l'implementazione compatibile con le versioni precedenti della versione precedente. L '"API di manutenzione" dovrebbe essere implementata sull'API corrente. In questo modo non sei pronto per mantenere diverse implementazioni complete; solo la versione corrente e diverse "shell" per le vecchie versioni. L'esecuzione di un test di regressione per l'API ora obsoleta rispetto al client compatibile con le versioni precedenti è un buon modo per testare sia la nuova API sia il livello di compatibilità.

3

La prima opzione di progettazione URI esprime meglio l'idea che stai eseguendo il controllo delle versioni dell'intera API. Il secondo potrebbe essere interpretato come il controllo delle versioni dei messaggi stessi. Quindi questo è meglio IMO:

rest.product.com/api/v1/messages/send

Per la libreria client penso che usare due implementazioni complete in pacchetti Java separati sia più pulito, più facile da usare e più facile da mantenere.

Detto questo, esistono metodi migliori per evolvere le API rispetto al controllo delle versioni. Sono d'accordo con te sul fatto che dovresti essere preparato per questo, ma penso al versioning come al metodo di ultima istanza, da usare con attenzione e parsimonia. È uno sforzo relativamente grande per l'aggiornamento da parte dei clienti. Qualche tempo fa ho inserito alcuni di questi pensieri in un post sul blog:

http://theamiableapi.com/2011/10/18/api-design-best-practice-plan-for-evolution/

Per la versione specifica dell'API REST, questo post di Mark Nottingham è utile:

http://www.mnot.net/blog/2011/10/25/web_api_versioning_smackdown


3

Un altro approccio per gestire il versioning delle API è utilizzare la versione nelle intestazioni HTTP. Piace

POST /messages/list/{user} HTTP/1.1
Host: http://rest.service.com
Content-Type: application/json
API-Version: 1.0      <----- like here
Cache-Control: no-cache

È possibile analizzare l'intestazione e gestirla in modo appropriato nel back-end.

Con questo approccio i client non devono modificare l'URL, ma solo l'intestazione. E anche questo rende gli endpoint REST più puliti, sempre.

Se uno dei client non ha inviato l'intestazione della versione, puoi inviare 400 - Richiesta non valida oppure puoi gestirla con una versione dell'API compatibile con le versioni precedenti .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.